From 5a85ad6dc7aa9b1ea94d787ec9cce32b9402898e Mon Sep 17 00:00:00 2001 From: Kris Thielemans Date: Mon, 14 Jul 2025 17:54:50 +0100 Subject: [PATCH 01/42] add PETSIRD submodule --- .gitmodules | 3 +++ PETSIRD | 1 + src/IO/CMakeLists.txt | 9 +++++++++ 3 files changed, 13 insertions(+) create mode 160000 PETSIRD diff --git a/.gitmodules b/.gitmodules index 5d1ac04565..7070550c65 100644 --- a/.gitmodules +++ b/.gitmodules @@ -1,3 +1,6 @@ [submodule "external_helpers/fmt"] path = external_helpers/fmt url = https://github.com/fmtlib/fmt.git +[submodule "PETSIRD"] + path = PETSIRD + url = https://github.com/ETSInitiative/PETSIRD diff --git a/PETSIRD b/PETSIRD new file mode 160000 index 0000000000..20487845e6 --- /dev/null +++ b/PETSIRD @@ -0,0 +1 @@ +Subproject commit 20487845e6e7c1b2535101f4147ad25c1ee413c1 diff --git a/src/IO/CMakeLists.txt b/src/IO/CMakeLists.txt index c40da974a5..036e61d58c 100644 --- a/src/IO/CMakeLists.txt +++ b/src/IO/CMakeLists.txt @@ -136,3 +136,12 @@ if (NOT MINI_STIR) target_link_libraries(IO PUBLIC modelling_buildblock ) target_link_libraries(IO PUBLIC listmode_buildblock) endif() + +set(PETSIRD_dir ../../PETSIRD/cpp/generated) +add_subdirectory(${PETSIRD_dir} PETSIRD_generated) + +target_include_directories(IO PUBLIC ${PETSIRD_dir}) +# needed for helpers +target_include_directories(IO PUBLIC ${PETSIRD_dir}/..) +target_include_directories(IO PUBLIC ${PETSIRD_dir}/../helpers/include) +target_link_libraries(IO PUBLIC petsird_generated) From 561616e6281fd38f7d17b12ef8a8cd02d2f83b28 Mon Sep 17 00:00:00 2001 From: NikEfth Date: Mon, 14 Jul 2025 19:57:57 -0400 Subject: [PATCH 02/42] * Initial commit that adds PETSIRD support in CMake configuration --- CMakeLists.txt | 66 ++++++++++++++++++++++++++++++++++++------- src/IO/CMakeLists.txt | 15 +++++----- 2 files changed, 63 insertions(+), 18 deletions(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index 73d5c2c3e2..5d14b93935 100755 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -92,17 +92,18 @@ find_package( Boost 1.36.0 REQUIRED ) #### optional external libraries. # Listed here such that we know if we should compile extra utilities -option(DISABLE_LLN_MATRIX "disable use of LLN library" OFF) -option(DISABLE_ITK "disable use of ITK library" OFF) -option(DISABLE_HDF5 "disable use of HDF5 libraries" 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_NLOHMANN_JSON "disable use of nlohmann JSON libraries" OFF) -option(STIR_ENABLE_EXPERIMENTAL "disable use of STIR experimental code" OFF) # disable by default -option(DISABLE_NiftyPET_PROJECTOR "disable use of NiftyPET projector" OFF) -option(DISABLE_Parallelproj_PROJECTOR "disable use of Parallelproj projector" OFF) +option(DISABLE_LLN_MATRIX "disable use of LLN library" ON) +option(DISABLE_ITK "disable use of ITK library" ON) +option(DISABLE_HDF5 "disable use of HDF5 libraries" ON) +option(DISABLE_STIR_LOCAL "disable use of LOCAL extensions to STIR" ON) +option(DISABLE_CERN_ROOT "disable use of Cern ROOT libraries" ON) +option(DISABLE_NLOHMANN_JSON "disable use of nlohmann JSON libraries" ON) +option(STIR_ENABLE_EXPERIMENTAL "disable use of STIR experimental code" ON) # disable by default +option(DISABLE_NiftyPET_PROJECTOR "disable use of NiftyPET projector" ON) +option(DISABLE_Parallelproj_PROJECTOR "disable use of Parallelproj projector" ON) OPTION(DOWNLOAD_ZENODO_TEST_DATA "download zenodo data for tests" OFF) -option(DISABLE_UPENN "disable use of UPENN filetypes" OFF) +option(DISABLE_UPENN "disable use of UPENN filetypes" ON) +option(DISABLE_PETSIRD "disable use of PETSIRD filetypes" OFF) find_package(Git QUIET) if(GIT_FOUND AND EXISTS "${PROJECT_SOURCE_DIR}/.git") @@ -259,6 +260,51 @@ else() message(STATUS "Parallelproj projector support disabled or not available.") endif() +if(NOT DISABLE_PETSIRD) + if(GIT_FOUND AND EXISTS "${PROJECT_SOURCE_DIR}/.git") + # Check if PETSIRD is already registered as a submodule + execute_process( + COMMAND ${GIT_EXECUTABLE} config --file ${PROJECT_SOURCE_DIR}/.gitmodules --get-regexp "submodule\\.PETSIRD\\.path" + RESULT_VARIABLE SUBMOD_EXISTS + OUTPUT_QUIET + ERROR_QUIET + ) + + if(NOT SUBMOD_EXISTS EQUAL 0) + message(STATUS "Adding PETSIRD submodule...") + execute_process( + COMMAND ${GIT_EXECUTABLE} submodule add https://github.com/ETSInitiative/PETSIRD.git PETSIRD + WORKING_DIRECTORY ${PROJECT_SOURCE_DIR} + RESULT_VARIABLE GIT_ADD_RESULT + ) + if(NOT GIT_ADD_RESULT EQUAL 0) + message(WARNING "Submodule add failed with code ${GIT_ADD_RESULT}") + endif() + else() + message(STATUS "PETSIRD submodule already exists in .gitmodules.") + endif() + + # Always update/init to ensure it's ready + execute_process( + COMMAND ${GIT_EXECUTABLE} submodule update --init --recursive + WORKING_DIRECTORY ${PROJECT_SOURCE_DIR} + ) + endif() + + execute_process( + COMMAND just generate + WORKING_DIRECTORY ${PROJECT_SOURCE_DIR}/PETSIRD + RESULT_VARIABLE JUST_RESULT + ) + if(JUST_RESULT EQUAL 0) + set(HAVE_PETSIRD TRUE) + message(STATUS "PETSIRD generation succeeded. HAVE_PETSIRD set to TRUE.") + else() + set(HAVE_PETSIRD FALSE) + message(WARNING "PETSIRD generation failed with code ${JUST_RESULT}. HAVE_PETSIRD not set.") + endif() +endif() + #### enable support for ctest ENABLE_TESTING() diff --git a/src/IO/CMakeLists.txt b/src/IO/CMakeLists.txt index 036e61d58c..b874d5cdd4 100644 --- a/src/IO/CMakeLists.txt +++ b/src/IO/CMakeLists.txt @@ -137,11 +137,10 @@ if (NOT MINI_STIR) target_link_libraries(IO PUBLIC listmode_buildblock) endif() -set(PETSIRD_dir ../../PETSIRD/cpp/generated) -add_subdirectory(${PETSIRD_dir} PETSIRD_generated) - -target_include_directories(IO PUBLIC ${PETSIRD_dir}) -# needed for helpers -target_include_directories(IO PUBLIC ${PETSIRD_dir}/..) -target_include_directories(IO PUBLIC ${PETSIRD_dir}/../helpers/include) -target_link_libraries(IO PUBLIC petsird_generated) +#if (HAVE_PETSIRD) + #target_include_directories(IO PUBLIC ${PETSIRD_dir}) + ## needed for helpers + #target_include_directories(IO PUBLIC ${PETSIRD_dir}/..) + #target_include_directories(IO PUBLIC ${PETSIRD_dir}/../helpers/include) + #target_link_libraries(IO PUBLIC petsird_generated) +#endif() From d6c218acb2a45bf4ad2dc1434b368c2ad01900dc Mon Sep 17 00:00:00 2001 From: NikEfth Date: Mon, 14 Jul 2025 21:10:12 -0400 Subject: [PATCH 03/42] WIP: Subclass CListModeDataSAFIR from CListModeDataBasedOnCoordinateMap. --- src/IO/CMakeLists.txt | 15 +- .../stir/IO/SAFIRCListmodeInputFileFormat.h | 8 +- .../CListModeDataBasedOnCoordinateMap.h | 101 +++++++++++++ .../stir/listmode/CListModeDataSAFIR.h | 80 ++++------- .../CListModeDataBasedOnCoordinateMap.cxx | 135 ++++++++++++++++++ .../CListModeDataSAFIR.cxx | 109 ++++---------- src/listmode_buildblock/CMakeLists.txt | 1 + 7 files changed, 306 insertions(+), 143 deletions(-) create mode 100644 src/include/stir/listmode/CListModeDataBasedOnCoordinateMap.h create mode 100644 src/listmode_buildblock/CListModeDataBasedOnCoordinateMap.cxx diff --git a/src/IO/CMakeLists.txt b/src/IO/CMakeLists.txt index b874d5cdd4..ea59e64a75 100644 --- a/src/IO/CMakeLists.txt +++ b/src/IO/CMakeLists.txt @@ -83,6 +83,7 @@ endif() # MINI_STIR include(stir_lib_target) + target_link_libraries(IO PRIVATE fmt) if (LLN_FOUND) @@ -137,10 +138,10 @@ if (NOT MINI_STIR) target_link_libraries(IO PUBLIC listmode_buildblock) endif() -#if (HAVE_PETSIRD) - #target_include_directories(IO PUBLIC ${PETSIRD_dir}) - ## needed for helpers - #target_include_directories(IO PUBLIC ${PETSIRD_dir}/..) - #target_include_directories(IO PUBLIC ${PETSIRD_dir}/../helpers/include) - #target_link_libraries(IO PUBLIC petsird_generated) -#endif() +if (HAVE_PETSIRD) + target_include_directories(IO PUBLIC ${PETSIRD_dir}) + ## needed for helpers + target_include_directories(IO PUBLIC ${PETSIRD_dir}/..) + target_include_directories(IO PUBLIC ${PETSIRD_dir}/../helpers/include) + target_link_libraries(IO PUBLIC petsird_generated) +endif() diff --git a/src/include/stir/IO/SAFIRCListmodeInputFileFormat.h b/src/include/stir/IO/SAFIRCListmodeInputFileFormat.h index 3042d4dd55..2ac3fdd96d 100644 --- a/src/include/stir/IO/SAFIRCListmodeInputFileFormat.h +++ b/src/include/stir/IO/SAFIRCListmodeInputFileFormat.h @@ -140,10 +140,10 @@ class SAFIRCListmodeInputFileFormat : public InputFileFormat, publ std::unique_ptr read_from_file(const std::string& filename) const override { - info("SAFIRCListmodeInputFileFormat: read_from_file(" + std::string(filename) + ")"); - actual_do_parsing(filename); - return std::unique_ptr(new CListModeDataSAFIR>( - listmode_filename, crystal_map_filename, template_proj_data_filename, lor_randomization_sigma)); + // info("SAFIRCListmodeInputFileFormat: read_from_file(" + std::string(filename) + ")"); + // actual_do_parsing(filename); + // return std::unique_ptr(new CListModeDataSAFIR>( + // listmode_filename, crystal_map_filename, template_proj_data_filename, lor_randomization_sigma)); } protected: diff --git a/src/include/stir/listmode/CListModeDataBasedOnCoordinateMap.h b/src/include/stir/listmode/CListModeDataBasedOnCoordinateMap.h new file mode 100644 index 0000000000..2ed6f211e0 --- /dev/null +++ b/src/include/stir/listmode/CListModeDataBasedOnCoordinateMap.h @@ -0,0 +1,101 @@ +/* CListModeDataSAFIR.h + + Coincidence LM Data Class for SAFIR: Header File + Jannis Fischer + + 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 + + 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. + + */ + +/*! + + \file + \ingroup listmode + \brief Declaration of class stir::CListModeDataSAFIR + + \author Jannis Fischer +*/ + +#ifndef __stir_listmode_CListModeDataBasedOnCoordinateMap_H__ +#define __stir_listmode_CListModeDataBasedOnCoordinateMap_H__ + +#include +#include +#include +#include + +#include "stir/listmode/CListModeData.h" +#include "stir/ProjData.h" +#include "stir/ProjDataInfo.h" +#include "stir/listmode/CListRecord.h" +#include "stir/IO/InputStreamWithRecords.h" +#include "stir/shared_ptr.h" + +#include "stir/listmode/CListRecordSAFIR.h" +#include "stir/DetectorCoordinateMap.h" + +START_NAMESPACE_STIR + +template +class CListModeDataBasedOnCoordinateMap : public CListModeData +{ +public: + /*! Constructor + \par + Takes as arguments the filenames of the coicidence listmode file, the crystal map (text) file, and the template projection data + file + */ + CListModeDataBasedOnCoordinateMap(const std::string& listmode_filename, + const std::string& crystal_map_filename, + const std::string& template_proj_data_filename, + const double lor_randomization_sigma = 0.0); + + CListModeDataBasedOnCoordinateMap(const std::string& listmode_filename, const shared_ptr& proj_data_info_sptr); + + std::string get_name() const override; + shared_ptr get_empty_record_sptr() const override; + Succeeded get_next_record(CListRecord& record_of_general_type) const override; + Succeeded reset() override; + + /*! + This function should save the position in input file. This is not implemented but disabled. + Returns 0 in the moement. + \todo Maybe provide real implementation? + */ + SavedPosition save_get_position() override { return static_cast(current_lm_data_ptr->save_get_position()); } + Succeeded set_get_position(const SavedPosition& pos) override { return current_lm_data_ptr->set_get_position(pos); } + + /*! + Returns just false in the moment. + \todo Implement this properly to check for delayed events in LM files. + */ + bool has_delayeds() const override { return false; } + +protected: + std::string listmode_filename; + mutable shared_ptr> current_lm_data_ptr; + mutable std::vector saved_get_positions; + virtual Succeeded open_lm_file() const = 0; + + shared_ptr map; +}; + + + + +END_NAMESPACE_STIR + +#endif diff --git a/src/include/stir/listmode/CListModeDataSAFIR.h b/src/include/stir/listmode/CListModeDataSAFIR.h index 66ca4344e7..6fcf424531 100644 --- a/src/include/stir/listmode/CListModeDataSAFIR.h +++ b/src/include/stir/listmode/CListModeDataSAFIR.h @@ -1,43 +1,44 @@ /* CListModeDataSAFIR.h - Coincidence LM Data Class for SAFIR: Header File - Jannis Fischer +Coincidence LM Data Class for SAFIR: Header File +Jannis Fischer - 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. */ /*! - \file - \ingroup listmode - \brief Declaration of class stir::CListModeDataSAFIR +\file +\ingroup listmode +\brief Declaration of class stir::CListModeDataSAFIR - \author Jannis Fischer +\author Jannis Fischer */ #ifndef __stir_listmode_CListModeDataSAFIR_H__ #define __stir_listmode_CListModeDataSAFIR_H__ + #include #include #include #include -#include "stir/listmode/CListModeData.h" +#include "stir/listmode/CListModeDataBasedOnCoordinateMap.h" #include "stir/ProjData.h" #include "stir/ProjDataInfo.h" #include "stir/listmode/CListRecord.h" @@ -45,7 +46,6 @@ #include "stir/shared_ptr.h" #include "stir/listmode/CListRecordSAFIR.h" -#include "stir/DetectorCoordinateMap.h" START_NAMESPACE_STIR @@ -57,47 +57,19 @@ START_NAMESPACE_STIR coordinates. */ template -class CListModeDataSAFIR : public CListModeData +class CListModeDataSAFIR : public CListModeDataBasedOnCoordinateMap { public: - /*! Constructor - \par - Takes as arguments the filenames of the coicidence listmode file, the crystal map (text) file, and the template projection data - file - */ + CListModeDataSAFIR(const std::string& listmode_filename, const std::string& crystal_map_filename, const std::string& template_proj_data_filename, const double lor_randomization_sigma = 0.0); - CListModeDataSAFIR(const std::string& listmode_filename, const shared_ptr& proj_data_info_sptr); - - std::string get_name() const override; - shared_ptr get_empty_record_sptr() const override; - Succeeded get_next_record(CListRecord& record_of_general_type) const override; - Succeeded reset() override; - - /*! - This function should save the position in input file. This is not implemented but disabled. - Returns 0 in the moement. - \todo Maybe provide real implementation? - */ - SavedPosition save_get_position() override { return static_cast(current_lm_data_ptr->save_get_position()); } - Succeeded set_get_position(const SavedPosition& pos) override { return current_lm_data_ptr->set_get_position(pos); } - - /*! - Returns just false in the moment. - \todo Implement this properly to check for delayed events in LM files. - */ - bool has_delayeds() const override { return false; } - -private: - std::string listmode_filename; - mutable shared_ptr> current_lm_data_ptr; - mutable std::vector saved_get_positions; - Succeeded open_lm_file() const; - shared_ptr map; + +protected: + virtual Succeeded open_lm_file() const; + }; END_NAMESPACE_STIR - -#endif +#endif // CLISTMODEDATASAFIR_H diff --git a/src/listmode_buildblock/CListModeDataBasedOnCoordinateMap.cxx b/src/listmode_buildblock/CListModeDataBasedOnCoordinateMap.cxx new file mode 100644 index 0000000000..439b9a8dd2 --- /dev/null +++ b/src/listmode_buildblock/CListModeDataBasedOnCoordinateMap.cxx @@ -0,0 +1,135 @@ +/* CListModeDataSAFIR.cxx + +Coincidence LM Data Class for SAFIR: Implementation + + Copyright 2015 ETH Zurich, Institute of Particle Physics + Copyright 2020 Positrigo AG, Zurich + Copyright 2021 University College London + + 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 + + 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. +*/ +/*! + + \file + \ingroup listmode + \brief implementation of class stir::CListModeDataSAFIR + + \author Jannis Fischer + \author Kris Thielemans + \author Markus Jehl +*/ +#include +#include +#include + +#include "stir/ExamInfo.h" +#include "stir/Succeeded.h" +#include "stir/info.h" +#include "stir/error.h" + +//#include "boost/static_assert.hpp" + +#include "stir/listmode/CListModeDataBasedOnCoordinateMap.h" +#include "stir/listmode/CListRecordSAFIR.h" + +using std::ios; +using std::fstream; +using std::ifstream; +using std::istream; + +START_NAMESPACE_STIR; + +template +CListModeDataBasedOnCoordinateMap::CListModeDataBasedOnCoordinateMap(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) +{ + if (!crystal_map_filename.empty()) + { + map = MAKE_SHARED(crystal_map_filename, lor_randomization_sigma); + } + else + { + if (lor_randomization_sigma != 0) + error("SAFIR currently does not support LOR-randomisation unless a map is specified"); + } + shared_ptr _exam_info_sptr(new ExamInfo); + _exam_info_sptr->imaging_modality = ImagingModality::PT; + this->exam_info_sptr = _exam_info_sptr; + + // 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 +CListModeDataBasedOnCoordinateMap::CListModeDataBasedOnCoordinateMap(const std::string& listmode_filename, + const shared_ptr& proj_data_info_sptr) + : listmode_filename(listmode_filename) +{ + shared_ptr _exam_info_sptr(new ExamInfo); + _exam_info_sptr->imaging_modality = ImagingModality::PT; + this->exam_info_sptr = _exam_info_sptr; + this->set_proj_data_info_sptr(proj_data_info_sptr->create_shared_clone()); + + if (open_lm_file() == Succeeded::no) + { + error("CListModeDataSAFIR: opening file \"" + listmode_filename + "\""); + } +} + +template +std::string +CListModeDataBasedOnCoordinateMap::get_name() const +{ + return listmode_filename; +} + +template +shared_ptr +CListModeDataBasedOnCoordinateMap::get_empty_record_sptr() const +{ + shared_ptr sptr(new CListRecordT); + sptr->event_SAFIR().set_scanner_sptr(this->get_proj_data_info_sptr()->get_scanner_sptr()); + sptr->event_SAFIR().set_map_sptr(map); + return static_pointer_cast(sptr); +} + +template +Succeeded +CListModeDataBasedOnCoordinateMap::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_sptr(map); + return status; +} + +template +Succeeded +CListModeDataBasedOnCoordinateMap::reset() +{ + return current_lm_data_ptr->reset(); +} + +// template class CListModeDataSAFIR>; +// template class CListModeDataSAFIR>; + +END_NAMESPACE_STIR diff --git a/src/listmode_buildblock/CListModeDataSAFIR.cxx b/src/listmode_buildblock/CListModeDataSAFIR.cxx index 96aa9a3cb3..79a6cbc10d 100644 --- a/src/listmode_buildblock/CListModeDataSAFIR.cxx +++ b/src/listmode_buildblock/CListModeDataSAFIR.cxx @@ -2,31 +2,31 @@ Coincidence LM Data Class for SAFIR: Implementation - Copyright 2015 ETH Zurich, Institute of Particle Physics - Copyright 2020 Positrigo AG, Zurich - Copyright 2021 University College London + Copyright 2015 ETH Zurich, Institute of Particle Physics + Copyright 2020 Positrigo AG, Zurich + Copyright 2021 University College London - 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. */ /*! - \file - \ingroup listmode - \brief implementation of class stir::CListModeDataSAFIR +\file +\ingroup listmode +\brief implementation of class stir::CListModeDataSAFIR - \author Jannis Fischer - \author Kris Thielemans - \author Markus Jehl +\author Jannis Fischer +\author Kris Thielemans +\author Markus Jehl */ #include #include @@ -51,14 +51,17 @@ 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) + const std::string& crystal_map_filename, + const std::string& template_proj_data_filename, + const double lor_randomization_sigma) + : CListModeDataBasedOnCoordinateMap(listmode_filename, + crystal_map_filename, + template_proj_data_filename, + lor_randomization_sigma) { if (!crystal_map_filename.empty()) { - map = MAKE_SHARED(crystal_map_filename, lor_randomization_sigma); + this->map = MAKE_SHARED(crystal_map_filename, lor_randomization_sigma); } else { @@ -69,78 +72,28 @@ CListModeDataSAFIR::CListModeDataSAFIR(const std::string& listmode _exam_info_sptr->imaging_modality = ImagingModality::PT; this->exam_info_sptr = _exam_info_sptr; - // Here we are reading the scanner data from the template projdata + // 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) + if (this->open_lm_file() == Succeeded::no) { error("CListModeDataSAFIR: Could not open listmode file " + listmode_filename + "\n"); } } -template -CListModeDataSAFIR::CListModeDataSAFIR(const std::string& listmode_filename, - const shared_ptr& proj_data_info_sptr) - : listmode_filename(listmode_filename) -{ - shared_ptr _exam_info_sptr(new ExamInfo); - _exam_info_sptr->imaging_modality = ImagingModality::PT; - this->exam_info_sptr = _exam_info_sptr; - this->set_proj_data_info_sptr(proj_data_info_sptr->create_shared_clone()); - - if (open_lm_file() == Succeeded::no) - { - error("CListModeDataSAFIR: opening file \"" + listmode_filename + "\""); - } -} - -template -std::string -CListModeDataSAFIR::get_name() const -{ - return listmode_filename; -} - -template -shared_ptr -CListModeDataSAFIR::get_empty_record_sptr() const -{ - shared_ptr sptr(new CListRecordT); - sptr->event_SAFIR().set_scanner_sptr(this->get_proj_data_info_sptr()->get_scanner_sptr()); - sptr->event_SAFIR().set_map_sptr(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_sptr(map); - return status; -} - -template -Succeeded -CListModeDataSAFIR::reset() -{ - return current_lm_data_ptr->reset(); -} - template Succeeded CListModeDataSAFIR::open_lm_file() const { - shared_ptr stream_ptr(new fstream(listmode_filename.c_str(), ios::in | ios::binary)); + shared_ptr stream_ptr(new fstream(this->listmode_filename.c_str(), ios::in | ios::binary)); if (!(*stream_ptr)) { return Succeeded::no; } - info("CListModeDataSAFIR: opening file \"" + listmode_filename + "\"", 2); + info("CListModeDataSAFIR: opening file \"" + this->listmode_filename + "\"", 2); stream_ptr->seekg((std::streamoff)32); - current_lm_data_ptr.reset( + this->current_lm_data_ptr.reset( new InputStreamWithRecords(stream_ptr, sizeof(CListTimeDataSAFIR), sizeof(CListTimeDataSAFIR), diff --git a/src/listmode_buildblock/CMakeLists.txt b/src/listmode_buildblock/CMakeLists.txt index 9ac6b1e752..dc96757e6c 100644 --- a/src/listmode_buildblock/CMakeLists.txt +++ b/src/listmode_buildblock/CMakeLists.txt @@ -14,6 +14,7 @@ set(${dir_LIB_SOURCES} LmToProjDataWithRandomRejection.cxx CListModeDataECAT8_32bit.cxx CListRecordECAT8_32bit.cxx + CListModeDataBasedOnCoordinateMap.cxx CListModeDataSAFIR.cxx ) From 8ff962a14eaa4b380b22e05735396d9a02e4e5a9 Mon Sep 17 00:00:00 2001 From: Nikos Efthimiou Date: Tue, 15 Jul 2025 08:17:28 -0400 Subject: [PATCH 04/42] Update .gitmodules --- .gitmodules | 3 --- 1 file changed, 3 deletions(-) diff --git a/.gitmodules b/.gitmodules index 7070550c65..5d1ac04565 100644 --- a/.gitmodules +++ b/.gitmodules @@ -1,6 +1,3 @@ [submodule "external_helpers/fmt"] path = external_helpers/fmt url = https://github.com/fmtlib/fmt.git -[submodule "PETSIRD"] - path = PETSIRD - url = https://github.com/ETSInitiative/PETSIRD From 557cf97c8b103a5437cd6ce637f94eeb74f34e0a Mon Sep 17 00:00:00 2001 From: NikEfth Date: Tue, 15 Jul 2025 08:26:42 -0400 Subject: [PATCH 05/42] Remove PETSIRD submodule from .gitmodules --- PETSIRD | 1 - 1 file changed, 1 deletion(-) delete mode 160000 PETSIRD diff --git a/PETSIRD b/PETSIRD deleted file mode 160000 index 20487845e6..0000000000 --- a/PETSIRD +++ /dev/null @@ -1 +0,0 @@ -Subproject commit 20487845e6e7c1b2535101f4147ad25c1ee413c1 From 493a0c0886e48286bfc7001925214bba113d1159 Mon Sep 17 00:00:00 2001 From: NikEfth Date: Tue, 15 Jul 2025 10:59:38 -0400 Subject: [PATCH 06/42] Fix the include and link paths --- CMakeLists.txt | 3 ++- src/IO/CMakeLists.txt | 27 ++++++++++++++++++++++----- 2 files changed, 24 insertions(+), 6 deletions(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index 5d14b93935..c2e9df9a29 100755 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -298,7 +298,8 @@ if(NOT DISABLE_PETSIRD) ) if(JUST_RESULT EQUAL 0) set(HAVE_PETSIRD TRUE) - message(STATUS "PETSIRD generation succeeded. HAVE_PETSIRD set to TRUE.") + set(PETSIRD_base_dir ${PROJECT_SOURCE_DIR}/PETSIRD/cpp/generated) + message(STATUS "PETSIRD generation succeeded. THE PATH is ${PETSIRD_base_dir}. HAVE_PETSIRD set to TRUE.") else() set(HAVE_PETSIRD FALSE) message(WARNING "PETSIRD generation failed with code ${JUST_RESULT}. HAVE_PETSIRD not set.") diff --git a/src/IO/CMakeLists.txt b/src/IO/CMakeLists.txt index ea59e64a75..39c5ccfa24 100644 --- a/src/IO/CMakeLists.txt +++ b/src/IO/CMakeLists.txt @@ -139,9 +139,26 @@ if (NOT MINI_STIR) endif() if (HAVE_PETSIRD) - target_include_directories(IO PUBLIC ${PETSIRD_dir}) - ## needed for helpers - target_include_directories(IO PUBLIC ${PETSIRD_dir}/..) - target_include_directories(IO PUBLIC ${PETSIRD_dir}/../helpers/include) - target_link_libraries(IO PUBLIC petsird_generated) +#set(PETSIRD_dir ../../PETSIRD/cpp/generated) +#add_subdirectory(${PETSIRD_dir} PETSIRD_generated) + +target_include_directories(IO PUBLIC + $ + $ +) + +target_include_directories(IO PUBLIC + $ + $ +) + +target_include_directories(IO PUBLIC + $ + $ +) + +target_link_libraries(IO PUBLIC petsird_generated) endif() + + + From 27ec9f4687a9e4fb68d473cef81c141cfc24d21c Mon Sep 17 00:00:00 2001 From: NikEfth Date: Tue, 15 Jul 2025 11:59:49 -0400 Subject: [PATCH 07/42] * Minor fix --- src/IO/CMakeLists.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/IO/CMakeLists.txt b/src/IO/CMakeLists.txt index 39c5ccfa24..6c70745e62 100644 --- a/src/IO/CMakeLists.txt +++ b/src/IO/CMakeLists.txt @@ -157,7 +157,7 @@ target_include_directories(IO PUBLIC $ ) -target_link_libraries(IO PUBLIC petsird_generated) +#target_link_libraries(IO PUBLIC petsird_generated) endif() From 0e0b71b322096f30fb0ae26a7e275ac362ee5b81 Mon Sep 17 00:00:00 2001 From: nmdicom-recon Date: Tue, 15 Jul 2025 17:07:28 +0100 Subject: [PATCH 08/42] creating classes for LM PETSIRD, mainly a copy at this stage --- .../stir/IO/PETSIRDCListmodeInputFileFormat.h | 213 ++++++++++++ .../stir/listmode/CListModeDataPETSIRD.h | 79 +++++ .../stir/listmode/CListRecordPETSIRD.h | 309 ++++++++++++++++++ .../stir/listmode/CListRecordPETSIRD.inl | 148 +++++++++ .../CListModeDataPETSIRD.cxx | 109 ++++++ src/listmode_buildblock/CMakeLists.txt | 8 +- 6 files changed, 865 insertions(+), 1 deletion(-) create mode 100644 src/include/stir/IO/PETSIRDCListmodeInputFileFormat.h create mode 100644 src/include/stir/listmode/CListModeDataPETSIRD.h create mode 100644 src/include/stir/listmode/CListRecordPETSIRD.h create mode 100644 src/include/stir/listmode/CListRecordPETSIRD.inl create mode 100644 src/listmode_buildblock/CListModeDataPETSIRD.cxx diff --git a/src/include/stir/IO/PETSIRDCListmodeInputFileFormat.h b/src/include/stir/IO/PETSIRDCListmodeInputFileFormat.h new file mode 100644 index 0000000000..e5e446ad2a --- /dev/null +++ b/src/include/stir/IO/PETSIRDCListmodeInputFileFormat.h @@ -0,0 +1,213 @@ +/* PETSIRDCListmodeInputFileFormat.h + + Class defining input file format for coincidence listmode data for PETSIRD. + + 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 + + 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. + */ + +/*! + + \file + \ingroup listmode + \brief Declaration of class stir::PETSIRDCListmodeInputFileFormat + + \author Jannis Fischer + \author Markus Jehl, Positrigo +*/ + +#ifndef __stir_IO_PETSIRDCListmodeInputFileFormat_H__ +#define __stir_IO_PETSIRDCListmodeInputFileFormat_H__ + +#include +#include +#include +#include + +#include "boost/algorithm/string.hpp" + +#include "stir/IO/InputFileFormat.h" +#include "stir/info.h" +#include "stir/error.h" +#include "stir/utilities.h" +#include "stir/ParsingObject.h" + +#include "stir/listmode/CListRecordPETSIRD.h" +#include "stir/listmode/CListModeDataPETSIRD.h" + +START_NAMESPACE_STIR + +/*! \brief Class for reading PETSIRD coincidence listmode data. + +It reads a parameter file, which refers to + - optional crystal map containing the mapping between detector index triple and cartesian coordinates of the crystal surfaces +(see DetectorCoordinateMap) + - the binary data file with the coincidence listmode data in PETSIRD format (see CListModeDataPETSIRD) + - a template projection data file, which defines the scanner + + If the map is not defined, the scanner detectors will be used. Otherwise, the nearest LOR of the scanner will be selected for +each event. + + An example of such a parameter file would be + \code + CListModeDataPETSIRD Parameters:= + listmode data filename:= listmode_input.clm.PETSIRD + template projection data filename:= + ; optional map specifying the actual location of the crystals + crystal map filename:= crystal_map.txt + ; optional random displacement of the LOR end-points in mm (only used of a map is present) + LOR randomization (Gaussian) sigma:=0 + END CListModeDataPETSIRD Parameters:= + \endcode + + The first 32 bytes of the binary file are interpreted as file signature and matched against the strings "MUPET CListModeData\0", +"PETSIRD CListModeData\0" and "NeuroLF CListModeData\0". If either is successfull, the class claims it can read the file format. The +rest of the file is read as records as specified as template parameter, e.g. CListRecordPETSIRD. +*/ +template +class PETSIRDCListmodeInputFileFormat : public InputFileFormat, public ParsingObject +{ +public: + PETSIRDCListmodeInputFileFormat() {} + const std::string get_name() const override { return "PETSIRD Coincidence Listmode File Format"; } + + //! Checks in binary data file for correct signature. + bool can_read(const FileSignature& signature, std::istream& input) const override + { + return false; // cannot read from istream + } + + //! Checks in binary data file for correct signature (can be either "PETSIRD CListModeData", "NeuroLF CListModeData" or "MUPET + //! CListModeData"). + bool can_read(const FileSignature& signature, const std::string& filename) const override + { + // Looking for the right key in the parameter file + std::ifstream par_file(filename.c_str()); + std::string key; + std::getline(par_file, key, ':'); + key = standardise_interfile_keyword(key); + if (key != std::string("clistmodedataPETSIRD parameters")) + { + return false; + } + if (!actual_do_parsing(filename)) + return false; + std::ifstream data_file(listmode_filename.c_str(), std::ios::binary); + char* buffer = new char[32]; + data_file.read(buffer, 32); + bool cr = false; + // depending on used template, check header of listmode file for correct format + if (std::is_same::value) + { + cr = (!strncmp(buffer, "MUPET CListModeData\0", 20) || !strncmp(buffer, "PETSIRD CListModeData\0", 20)); + } + else if (std::is_same::value) + { + cr = !strncmp(buffer, "NeuroLF CListModeData\0", 20); + } + else + { + warning("PETSIRDCListModeInputFileFormat was initialised with an unexpected template."); + } + + if (!cr) + { + warning("PETSIRDCListModeInputFileFormat tried to read file " + listmode_filename + + " but it seems to have the wrong signature."); + } + + delete[] buffer; + return cr; + } + + std::unique_ptr read_from_file(std::istream& input) const override + { + error("read_from_file for PETSIRDCListmodeData with istream not implemented %s:%d. Sorry", __FILE__, __LINE__); + return unique_ptr(); + } + + std::unique_ptr read_from_file(const std::string& filename) const override + { + // info("PETSIRDCListmodeInputFileFormat: read_from_file(" + std::string(filename) + ")"); + // actual_do_parsing(filename); + // return std::unique_ptr(new CListModeDataPETSIRD>( + // listmode_filename, crystal_map_filename, template_proj_data_filename, lor_randomization_sigma)); + } + +protected: + typedef ParsingObject base_type; + mutable std::string listmode_filename; + mutable std::string crystal_map_filename; + mutable std::string template_proj_data_filename; + mutable double lor_randomization_sigma; + + bool actual_can_read(const FileSignature& signature, std::istream& input) const override + { + return false; // cannot read from istream + } + + void initialise_keymap() override + { + base_type::initialise_keymap(); + this->parser.add_start_key("CListModeDataPETSIRD Parameters"); + this->parser.add_key("listmode data filename", &listmode_filename); + this->parser.add_key("crystal map filename", &crystal_map_filename); + this->parser.add_key("template projection data filename", &template_proj_data_filename); + this->parser.add_key("LOR randomization (Gaussian) sigma", &lor_randomization_sigma); + this->parser.add_stop_key("END CListModeDataPETSIRD Parameters"); + } + + void set_defaults() override + { + base_type::set_defaults(); + crystal_map_filename = ""; + template_proj_data_filename = ""; + lor_randomization_sigma = 0.0; + } + + bool actual_do_parsing(const std::string& filename) const + { + // Ugly const_casts here, but I don't see an other nice way to use the parser + if (const_cast*>(this)->parse(filename.c_str())) + { + info(const_cast*>(this)->parameter_info()); + return true; + } + else + return false; + } + + bool post_processing() override + { + if (!file_exists(listmode_filename)) + return true; + else if (!file_exists(template_proj_data_filename)) + return true; + else + { + return false; + } + return true; + } + +private: + bool file_exists(const std::string& filename) + { + std::ifstream infile(filename.c_str()); + return infile.good(); + } +}; +END_NAMESPACE_STIR +#endif diff --git a/src/include/stir/listmode/CListModeDataPETSIRD.h b/src/include/stir/listmode/CListModeDataPETSIRD.h new file mode 100644 index 0000000000..70ad8d66ee --- /dev/null +++ b/src/include/stir/listmode/CListModeDataPETSIRD.h @@ -0,0 +1,79 @@ +/* CListModeDataPETSIRD.h + +Coincidence LM Data Class for PETSIRD: Header File +Jannis Fischer + + Copyright 2015 ETH Zurich, Institute of Particle Physics + Copyright 2020 Positrigo AG, Zurich + Copyright 2025 National Physical Laboratory + + 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 + + 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. + + */ + +/*! + +\file +\ingroup listmode +\brief Declaration of class stir::CListModeDataPETSIRD + +\author Daniel Deidda +*/ + +#ifndef __stir_listmode_CListModeDataPETSIRD_H__ +#define __stir_listmode_CListModeDataPETSIRD_H__ + + +#include +#include +#include +#include + +#include "stir/listmode/CListModeDataBasedOnCoordinateMap.h" +#include "stir/ProjData.h" +#include "stir/ProjDataInfo.h" +#include "stir/listmode/CListRecord.h" +#include "stir/IO/InputStreamWithRecords.h" +#include "stir/shared_ptr.h" +#include "petsird_helpers.h" +#include "petsird_helpers/create.h" +#include "petsird_helpers/geometry.h" + +#include "stir/listmode/CListRecordPETSIRD.h" + +START_NAMESPACE_STIR + +/*! + \brief Class for reading PETSIRD listmode data with variable geometry + \ingroup listmode + \par + By providing crystal map and template projection data files, the coordinates are read from files and used defining the LOR + coordinates. +*/ +template +class CListModeDataPETSIRD : public CListModeDataBasedOnCoordinateMap +{ +public: + + CListModeDataPETSIRD(const std::string& listmode_filename, + const std::string& crystal_map_filename, + const std::string& template_proj_data_filename, + const double lor_randomization_sigma = 0.0); + +protected: + virtual Succeeded open_lm_file() const; + +}; + +END_NAMESPACE_STIR +#endif // CLISTMODEDATAPETSIRD_H diff --git a/src/include/stir/listmode/CListRecordPETSIRD.h b/src/include/stir/listmode/CListRecordPETSIRD.h new file mode 100644 index 0000000000..4fd54d85fd --- /dev/null +++ b/src/include/stir/listmode/CListRecordPETSIRD.h @@ -0,0 +1,309 @@ +/* CListRecordPETSIRD.h + + Coincidence Event Class for PETSIRD: Header File + + Copyright 2015 ETH Zurich, Institute of Particle Physics + Copyright 2017 ETH Zurich, Institute of Particle Physics and Astrophysics + Copyright 2020, 2022 Positrigo AG, Zurich + Copyright 2025 National Physical Laboratory + + 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 + + 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. + + */ + +/*! + + \file + \ingroup listmode + \brief Declaration of class stir::CListEventPETSIRD and stir::CListRecordPETSIRD with supporting classes + + \author Jannis Fischer + \author Parisa Khateri + \author Markus Jehl + \author Daniel Deidda +*/ + +#ifndef __stir_listmode_CListRecordPETSIRD_H__ +#define __stir_listmode_CListRecordPETSIRD_H__ + +#include + +#include "stir/listmode/CListRecord.h" +#include "stir/DetectionPositionPair.h" +#include "stir/Succeeded.h" +#include "stir/ByteOrder.h" +#include "stir/ByteOrderDefine.h" + +#include "boost/static_assert.hpp" +#include "boost/cstdint.hpp" + +#include "stir/DetectorCoordinateMap.h" +#include "boost/make_shared.hpp" +#include "petsird_helpers.h" +#include "petsird_helpers/create.h" +#include "petsird_helpers/geometry.h" + +START_NAMESPACE_STIR + +/*! +Provides interface of the record class to STIR by implementing get_LOR(). It uses an optional map from detector indices to +coordinates to specify LORAs2Points from given detection pair indices. + +The record has the following format (for little-endian byte order) +\code + unsigned ringA : 8; + unsigned ringB : 8; + unsigned detA : 16; + unsigned detB : 16; + unsigned layerA : 4; + unsigned layerB : 4; + unsigned reserved : 6; + unsigned isDelayed : 1; + unsigned type : 1; +\endcode + \ingroup listmode +*/ +template +class CListEventPETSIRD : public CListEvent +{ +public: + /*! Default constructor will not work as it does not initialize a map to relate + detector indices and space coordinates. Always use either set_scanner_sptr or set_map_sptr after default construction. + */ + inline CListEventPETSIRD() {} + + //! Returns LOR corresponding to the given event. + inline LORAs2Points get_LOR() const override; + + //! Override the default implementation + inline void get_bin(Bin& bin, const ProjDataInfo& proj_data_info) const override; + + //! This method checks if the template is valid for LmToProjData + /*! Used before the actual processing of the data (see issue #61), before calling get_bin() + * Most scanners have listmode data that correspond to non arc-corrected data and + * this check avoids a crash when an unsupported template is used as input. + */ + inline bool is_valid_template(const ProjDataInfo&) const override { return true; } + + //! Returns 0 if event is prompt and 1 if delayed + inline bool is_prompt() const override { return !(static_cast(this)->is_prompt()); } + //! Function to set map for detector indices to coordinates. + /*! Use a null pointer to disable the mapping functionality */ + inline void set_map_sptr(shared_ptr new_map_sptr) { map_sptr = new_map_sptr; } + /*! Set the scanner */ + /*! Currently only used if the map is not set. */ + inline void set_scanner_sptr(shared_ptr new_scanner_sptr) { scanner_sptr = new_scanner_sptr; } + +private: + shared_ptr map_sptr; + shared_ptr scanner_sptr; + + const DetectorCoordinateMap& map_to_use() const { return map_sptr ? *map_sptr : *this->scanner_sptr->get_detector_map_sptr(); } +}; + +//! Class for record with coincidence data using PETSIRD bitfield definition +/*! \ingroup listmode */ +class CListEventDataPETSIRD +{ +public: + //! Writes detection position pair to reference given as argument. + inline void get_detection_position_pair(DetectionPositionPair<>& det_pos_pair); + + //! Returns 0 if event is prompt and 1 if delayed + inline bool is_prompt() const { return !isDelayed; } + + //! Returns 1 if if event is time and 0 if it is prompt + inline bool is_time() const { return type; } + + //! Can be used to set "promptness" of event. + inline Succeeded set_prompt(const bool prompt = true) + { + isDelayed = !prompt; + return Succeeded::yes; + } + +private: +#if STIRIsNativeByteOrderBigEndian + unsigned type : 1; + unsigned isDelayed : 1; + unsigned reserved : 6; + unsigned layerB : 4; + unsigned layerA : 4; + unsigned detB : 16; + unsigned detA : 16; + unsigned ringB : 8; + unsigned ringA : 8; +#else + unsigned ringA : 8; + unsigned ringB : 8; + unsigned detA : 16; + unsigned detB : 16; + unsigned layerA : 4; + unsigned layerB : 4; + unsigned reserved : 6; + unsigned isDelayed : 1; + unsigned type : 1; +#endif +}; + +//! Class for record with coincidence data using NeuroLF bitfield definition +/*! \ingroup listmode */ +class CListEventDataNeuroLF +{ +public: + //! Writes detection position pair to reference given as argument. + inline void get_detection_position_pair(DetectionPositionPair<>& det_pos_pair); + + //! Returns 0 if event is prompt and 1 if delayed + inline bool is_prompt() const { return !isDelayed; } + + //! Returns 1 if if event is time and 0 if it is prompt + inline bool is_time() const { return type; } + + //! Can be used to set "promptness" of event. + inline Succeeded set_prompt(const bool prompt = true) + { + isDelayed = !prompt; + return Succeeded::yes; + } + +private: +#if STIRIsNativeByteOrderBigEndian + unsigned type : 1; + unsigned isDelayed : 1; + unsigned reserved : 8; + unsigned layerB : 3; + unsigned layerA : 3; + unsigned detB : 16; + unsigned detA : 16; + unsigned ringB : 8; + unsigned ringA : 8; +#else + unsigned ringA : 8; + unsigned ringB : 8; + unsigned detA : 16; + unsigned detB : 16; + unsigned layerA : 3; + unsigned layerB : 3; + unsigned reserved : 8; + unsigned isDelayed : 1; + unsigned type : 1; +#endif +}; + +//! Class for record with time data using PETSIRD bitfield definition +/*! \ingroup listmode */ +class CListTimeDataPETSIRD +{ +public: + inline unsigned long get_time_in_millisecs() const { return static_cast(time); } + inline Succeeded set_time_in_millisecs(const unsigned long time_in_millisecs) + { + time = ((boost::uint64_t(1) << 49) - 1) & static_cast(time_in_millisecs); + return Succeeded::yes; + } + inline bool is_time() const { return type; } + +private: +#if STIRIsNativeByteOrderBigEndian + boost::uint64_t type : 1; + boost::uint64_t reserved : 15; + boost::uint64_t time : 48; +#else + boost::uint64_t time : 48; + boost::uint64_t reserved : 15; + boost::uint64_t type : 1; +#endif +}; + +//! Class for general PETSIRD record, containing a union of data, time and raw record and providing access to certain elements. +/*! \ingroup listmode */ +template +class CListRecordPETSIRD : public CListRecord, public ListTime, public CListEventPETSIRD> +{ +public: + //! Returns event_data (without checking if the type is really event and not time). + DataType get_data() const { return this->event_data; } + + CListRecordPETSIRD() + : CListEventPETSIRD>() + {} + + ~CListRecordPETSIRD() override {} + + bool is_time() const override { return time_data.is_time(); } + + bool is_event() const override { return !time_data.is_time(); } + + CListEvent& event() override { return *this; } + + const CListEvent& event() const override { return *this; } + + virtual CListEventPETSIRD>& event_PETSIRD() { return *this; } + + virtual const CListEventPETSIRD>& event_PETSIRD() const { return *this; } + + ListTime& time() override { return *this; } + + const ListTime& time() const override { return *this; } + + virtual bool operator==(const CListRecord& e2) const + { + return dynamic_cast const*>(&e2) != 0 + && raw == static_cast const&>(e2).raw; + } + + inline unsigned long get_time_in_millisecs() const override { return time_data.get_time_in_millisecs(); } + + inline Succeeded set_time_in_millisecs(const unsigned long time_in_millisecs) override + { + return time_data.set_time_in_millisecs(time_in_millisecs); + } + + inline bool is_prompt() const override { return event_data.is_prompt(); } + + Succeeded init_from_data_ptr(const char* const data_ptr, const std::size_t size_of_record, const bool do_byte_swap) + { + assert(size_of_record >= 8); + std::copy(data_ptr, data_ptr + 8, reinterpret_cast(&raw)); // TODO necessary for operator== + if (do_byte_swap) + ByteOrder::swap_order(raw); + return Succeeded::yes; + } + + std::size_t size_of_record_at_ptr(const char* const /*data_ptr*/, const std::size_t /*size*/, const bool /*do_byte_swap*/) const + { + return 8; + } + +private: + // use C++ union to save data, you can only use one at a time, + // but compiler will not check which one was used! + // Be careful not to read event data from time record and vice versa!! + // However, this is used as a feature if comparing events over the 'raw' type. + union + { + DataType event_data; + CListTimeDataPETSIRD time_data; + boost::int64_t raw; + }; + BOOST_STATIC_ASSERT(sizeof(boost::uint64_t) == 8); + BOOST_STATIC_ASSERT(sizeof(DataType) == 8); + BOOST_STATIC_ASSERT(sizeof(CListTimeDataPETSIRD) == 8); +}; + +END_NAMESPACE_STIR + +#include "CListRecordPETSIRD.inl" + +#endif diff --git a/src/include/stir/listmode/CListRecordPETSIRD.inl b/src/include/stir/listmode/CListRecordPETSIRD.inl new file mode 100644 index 0000000000..56b426aa8d --- /dev/null +++ b/src/include/stir/listmode/CListRecordPETSIRD.inl @@ -0,0 +1,148 @@ +/* CListRecordPETSIRD.inl + + Coincidence Event Class for PETSIRD: Inline File + + Copyright 2015 ETH Zurich, Institute of Particle Physics + Copyright 2017 ETH Zurich, Institute of Particle Physics and Astrophysics + Copyright 2020, 2022 Positrigo AG, Zurich + Copyright 2021 University College London + Copyright 2025 National Physical Laboratory + + 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 + + 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. + + */ +/*! + + \file + \ingroup listmode + \brief Inline implementation of class stir::CListEventPETSIRD and stir::CListRecordPETSIRD with supporting classes + + \author Jannis Fischer + \author Parisa Khateri + \author Markus Jehl + \author Kris Thielemans + \author Daniel Deidda +*/ + +#include + +#include "stir/LORCoordinates.h" +#include "stir/listmode/CListRecord.h" +#include "stir/ProjDataInfo.h" +#include "stir/Bin.h" +#include "stir/LORCoordinates.h" +#include "stir/Succeeded.h" + +#include "stir/ProjDataInfoCylindricalNoArcCorr.h" +#include "stir/ProjDataInfoBlocksOnCylindricalNoArcCorr.h" +#include "stir/ProjDataInfoGenericNoArcCorr.h" +#include "stir/CartesianCoordinate3D.h" +#include "stir/error.h" + +START_NAMESPACE_STIR + +template +LORAs2Points +CListEventPETSIRD::get_LOR() const +{ + LORAs2Points lor; + DetectionPositionPair<> det_pos_pair; + + static_cast(this)->get_data().get_detection_position_pair(det_pos_pair); + + lor.p1() = map_to_use().get_coordinate_for_index(det_pos_pair.pos1()); + lor.p2() = map_to_use().get_coordinate_for_index(det_pos_pair.pos2()); + + return lor; +} + +namespace detail +{ +template +static inline bool +get_bin_for_det_pos_pair(Bin& bin, DetectionPositionPair<>& det_pos_pair, const ProjDataInfo& proj_data_info) +{ + if (auto proj_data_info_ptr = dynamic_cast(&proj_data_info)) + { + if (proj_data_info_ptr->get_bin_for_det_pos_pair(bin, det_pos_pair) == Succeeded::yes) + bin.set_bin_value(1); + else + bin.set_bin_value(-1); + return true; + } + else + return false; +} +} // namespace detail + +template +void +CListEventPETSIRD::get_bin(Bin& bin, const ProjDataInfo& proj_data_info) const +{ + DetectionPositionPair<> det_pos_pair; + static_cast(this)->get_data().get_detection_position_pair(det_pos_pair); + + if (!map_sptr) + { + // transform det_pos_pair into stir conventions + det_pos_pair.pos1() = map_to_use().get_det_pos_for_index(det_pos_pair.pos1()); + det_pos_pair.pos2() = map_to_use().get_det_pos_for_index(det_pos_pair.pos2()); + + if (det_pos_pair.pos1().tangential_coord() == det_pos_pair.pos2().tangential_coord()) + { + bin.set_bin_value(-1); + return; + } + + if (!detail::get_bin_for_det_pos_pair(bin, det_pos_pair, proj_data_info)) + { + if (!detail::get_bin_for_det_pos_pair(bin, det_pos_pair, proj_data_info)) + error("Wrong type of proj-data-info for PETSIRD"); + } + } + else + { + const stir::CartesianCoordinate3D c1 = map_sptr->get_coordinate_for_index(det_pos_pair.pos1()); + const stir::CartesianCoordinate3D c2 = map_sptr->get_coordinate_for_index(det_pos_pair.pos2()); + const LORAs2Points lor(c1, c2); + bin = proj_data_info.get_bin(lor); + } +} + +void +CListEventDataPETSIRD::get_detection_position_pair(DetectionPositionPair<>& det_pos_pair) +{ + det_pos_pair.pos1().radial_coord() = layerA; + det_pos_pair.pos2().radial_coord() = layerB; + + det_pos_pair.pos1().axial_coord() = ringA; + det_pos_pair.pos2().axial_coord() = ringB; + + det_pos_pair.pos1().tangential_coord() = detA; + det_pos_pair.pos2().tangential_coord() = detB; +} + +void +CListEventDataNeuroLF::get_detection_position_pair(DetectionPositionPair<>& det_pos_pair) +{ + det_pos_pair.pos1().radial_coord() = layerA; + det_pos_pair.pos2().radial_coord() = layerB; + + det_pos_pair.pos1().axial_coord() = ringA; + det_pos_pair.pos2().axial_coord() = ringB; + + det_pos_pair.pos1().tangential_coord() = detA; + det_pos_pair.pos2().tangential_coord() = detB; +} + +END_NAMESPACE_STIR diff --git a/src/listmode_buildblock/CListModeDataPETSIRD.cxx b/src/listmode_buildblock/CListModeDataPETSIRD.cxx new file mode 100644 index 0000000000..225332d5d7 --- /dev/null +++ b/src/listmode_buildblock/CListModeDataPETSIRD.cxx @@ -0,0 +1,109 @@ +/* CListModeDataPETSIRD.cxx + +Coincidence LM Data Class for PETSIRD: Implementation + + Copyright 2015 ETH Zurich, Institute of Particle Physics + Copyright 2020 Positrigo AG, Zurich + Copyright 2021 University College London + Copyright 2025 National Physical Laboratory + + + 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 + + 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. +*/ +/*! + +\file +\ingroup listmode +\brief implementation of class stir::CListModeDataPETSIRD + +\author Jannis Fischer +\author Kris Thielemans +\author Markus Jehl +\author Daniel Deidda +*/ +#include +#include +#include + +#include "stir/ExamInfo.h" +#include "stir/Succeeded.h" +#include "stir/info.h" +#include "stir/error.h" + +//#include "boost/static_assert.hpp" + +#include "stir/listmode/CListModeDataPETSIRD.h" +#include "stir/listmode/CListRecordPETSIRD.h" + +using std::ios; +using std::fstream; +using std::ifstream; +using std::istream; + +START_NAMESPACE_STIR; + +template +CListModeDataPETSIRD::CListModeDataPETSIRD(const std::string& listmode_filename, + const std::string& crystal_map_filename, + const std::string& template_proj_data_filename, + const double lor_randomization_sigma) + : CListModeDataBasedOnCoordinateMap(listmode_filename, + crystal_map_filename, + template_proj_data_filename, + lor_randomization_sigma) +{ + if (!crystal_map_filename.empty()) + { + this->map = MAKE_SHARED(crystal_map_filename, lor_randomization_sigma); + } + else + { + if (lor_randomization_sigma != 0) + error("PETSIRD currently does not support LOR-randomisation unless a map is specified"); + } + shared_ptr _exam_info_sptr(new ExamInfo); + _exam_info_sptr->imaging_modality = ImagingModality::PT; + this->exam_info_sptr = _exam_info_sptr; + + // 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 (this->open_lm_file() == Succeeded::no) + { + error("CListModeDataPETSIRD: Could not open listmode file " + listmode_filename + "\n"); + } +} + +template +Succeeded +CListModeDataPETSIRD::open_lm_file() const +{ + shared_ptr stream_ptr(new fstream(this->listmode_filename.c_str(), ios::in | ios::binary)); + if (!(*stream_ptr)) + { + return Succeeded::no; + } + info("CListModeDataPETSIRD: opening file \"" + this->listmode_filename + "\"", 2); + stream_ptr->seekg((std::streamoff)32); + this->current_lm_data_ptr.reset( + new InputStreamWithRecords(stream_ptr, + sizeof(CListTimeDataPETSIRD), + sizeof(CListTimeDataPETSIRD), + ByteOrder::little_endian != ByteOrder::get_native_order())); + return Succeeded::yes; +} + +template class CListModeDataPETSIRD>; + +END_NAMESPACE_STIR diff --git a/src/listmode_buildblock/CMakeLists.txt b/src/listmode_buildblock/CMakeLists.txt index dc96757e6c..4497205b89 100644 --- a/src/listmode_buildblock/CMakeLists.txt +++ b/src/listmode_buildblock/CMakeLists.txt @@ -15,7 +15,7 @@ set(${dir_LIB_SOURCES} CListModeDataECAT8_32bit.cxx CListRecordECAT8_32bit.cxx CListModeDataBasedOnCoordinateMap.cxx - CListModeDataSAFIR.cxx + CListModeDataSAFIR.cxx ) if (HAVE_HDF5) @@ -25,6 +25,12 @@ list(APPEND ${dir_LIB_SOURCES} ) endif() +if (HAVE_PETSIRD) +list(APPEND ${dir_LIB_SOURCES} + CListModeDataPETSIRD.cxx +) +endif() + if (HAVE_ECAT) list(APPEND ${dir_LIB_SOURCES} CListModeDataECAT.cxx From 6d00cde5bd5c0f5b83b645824b59abb51575fa2f Mon Sep 17 00:00:00 2001 From: nmdicom-recon Date: Wed, 16 Jul 2025 09:14:52 +0100 Subject: [PATCH 09/42] move cmake importing of sird into lmbuildblock remove: unnecessary bits from classes --- src/IO/CMakeLists.txt | 32 ++++---- .../stir/IO/PETSIRDCListmodeInputFileFormat.h | 74 ++++++++++--------- .../stir/listmode/CListModeDataPETSIRD.h | 3 - .../stir/listmode/CListRecordPETSIRD.h | 70 +----------------- .../CListModeDataPETSIRD.cxx | 11 ++- src/listmode_buildblock/CMakeLists.txt | 21 ++++++ 6 files changed, 88 insertions(+), 123 deletions(-) diff --git a/src/IO/CMakeLists.txt b/src/IO/CMakeLists.txt index 6c70745e62..94ac06efaa 100644 --- a/src/IO/CMakeLists.txt +++ b/src/IO/CMakeLists.txt @@ -138,27 +138,27 @@ if (NOT MINI_STIR) target_link_libraries(IO PUBLIC listmode_buildblock) endif() -if (HAVE_PETSIRD) -#set(PETSIRD_dir ../../PETSIRD/cpp/generated) -#add_subdirectory(${PETSIRD_dir} PETSIRD_generated) +#if (HAVE_PETSIRD) +##set(PETSIRD_dir ../../PETSIRD/cpp/generated) +##add_subdirectory(${PETSIRD_dir} PETSIRD_generated) -target_include_directories(IO PUBLIC - $ - $ -) +#target_include_directories(IO PUBLIC +# $ +# $ +#) -target_include_directories(IO PUBLIC - $ - $ -) +#target_include_directories(IO PUBLIC +# $ +# $ +#) -target_include_directories(IO PUBLIC - $ - $ -) +#target_include_directories(IO PUBLIC +# $ +# $ +#) #target_link_libraries(IO PUBLIC petsird_generated) -endif() +#endif() diff --git a/src/include/stir/IO/PETSIRDCListmodeInputFileFormat.h b/src/include/stir/IO/PETSIRDCListmodeInputFileFormat.h index e5e446ad2a..905cc80f28 100644 --- a/src/include/stir/IO/PETSIRDCListmodeInputFileFormat.h +++ b/src/include/stir/IO/PETSIRDCListmodeInputFileFormat.h @@ -4,6 +4,7 @@ Copyright 2015 ETH Zurich, Institute of Particle Physics Copyright 2020 Positrigo AG, Zurich + Copyright 2025 National Physical Laboratory Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. @@ -26,6 +27,8 @@ \author Jannis Fischer \author Markus Jehl, Positrigo + \author Daniel Deidda + */ #ifndef __stir_IO_PETSIRDCListmodeInputFileFormat_H__ @@ -39,6 +42,8 @@ #include "boost/algorithm/string.hpp" #include "stir/IO/InputFileFormat.h" +#include "stir/IO/InputFileFormat.h" +#include "stir/IO/interfile.h" #include "stir/info.h" #include "stir/error.h" #include "stir/utilities.h" @@ -51,37 +56,16 @@ START_NAMESPACE_STIR /*! \brief Class for reading PETSIRD coincidence listmode data. -It reads a parameter file, which refers to - - optional crystal map containing the mapping between detector index triple and cartesian coordinates of the crystal surfaces -(see DetectorCoordinateMap) - - the binary data file with the coincidence listmode data in PETSIRD format (see CListModeDataPETSIRD) - - a template projection data file, which defines the scanner - - If the map is not defined, the scanner detectors will be used. Otherwise, the nearest LOR of the scanner will be selected for -each event. - - An example of such a parameter file would be - \code - CListModeDataPETSIRD Parameters:= - listmode data filename:= listmode_input.clm.PETSIRD - template projection data filename:= - ; optional map specifying the actual location of the crystals - crystal map filename:= crystal_map.txt - ; optional random displacement of the LOR end-points in mm (only used of a map is present) - LOR randomization (Gaussian) sigma:=0 - END CListModeDataPETSIRD Parameters:= - \endcode - The first 32 bytes of the binary file are interpreted as file signature and matched against the strings "MUPET CListModeData\0", -"PETSIRD CListModeData\0" and "NeuroLF CListModeData\0". If either is successfull, the class claims it can read the file format. The -rest of the file is read as records as specified as template parameter, e.g. CListRecordPETSIRD. +"PETSIRD". If either is successfull, the class claims it can read the file format. The +rest of the file is read as records, e.g. CListRecordPETSIRD. */ template class PETSIRDCListmodeInputFileFormat : public InputFileFormat, public ParsingObject { public: PETSIRDCListmodeInputFileFormat() {} - const std::string get_name() const override { return "PETSIRD Coincidence Listmode File Format"; } + const std::string get_name() const override { return "PETSIRD"; } //! Checks in binary data file for correct signature. bool can_read(const FileSignature& signature, std::istream& input) const override @@ -138,25 +122,47 @@ class PETSIRDCListmodeInputFileFormat : public InputFileFormat, pu return unique_ptr(); } - std::unique_ptr read_from_file(const std::string& filename) const override +// std::unique_ptr read_from_file(const std::string& filename) const override +// { + + +// // info("PETSIRDCListmodeInputFileFormat: read_from_file(" + std::string(filename) + ")"); +// // actual_do_parsing(filename); +// // return std::unique_ptr(new CListModeDataPETSIRD>( +// // listmode_filename, crystal_map_filename, template_proj_data_filename, lor_randomization_sigma)); +// } +protected: + + virtual bool + actual_can_read(const FileSignature& signature, + std::istream &input) const { - // info("PETSIRDCListmodeInputFileFormat: read_from_file(" + std::string(filename) + ")"); - // actual_do_parsing(filename); - // return std::unique_ptr(new CListModeDataPETSIRD>( - // listmode_filename, crystal_map_filename, template_proj_data_filename, lor_randomization_sigma)); + if(!is_interfile_signature(signature.get_signature())) + return false; + else + { + const std::string signature_as_string(signature.get_signature(), signature.size()); + return signature_as_string.find("PETSIRD") != std::string::npos; + } + } + + + virtual unique_ptr read_from_file( + const std::string& filename) const + { + return unique_ptr(new CListModeDataPETSIRD(filename)); } -protected: typedef ParsingObject base_type; mutable std::string listmode_filename; mutable std::string crystal_map_filename; mutable std::string template_proj_data_filename; mutable double lor_randomization_sigma; - bool actual_can_read(const FileSignature& signature, std::istream& input) const override - { - return false; // cannot read from istream - } +// bool actual_can_read(const FileSignature& signature, std::istream& input) const override +// { +// return false; // cannot read from istream +// } void initialise_keymap() override { diff --git a/src/include/stir/listmode/CListModeDataPETSIRD.h b/src/include/stir/listmode/CListModeDataPETSIRD.h index 70ad8d66ee..1db3b3b422 100644 --- a/src/include/stir/listmode/CListModeDataPETSIRD.h +++ b/src/include/stir/listmode/CListModeDataPETSIRD.h @@ -45,9 +45,6 @@ Jannis Fischer #include "stir/listmode/CListRecord.h" #include "stir/IO/InputStreamWithRecords.h" #include "stir/shared_ptr.h" -#include "petsird_helpers.h" -#include "petsird_helpers/create.h" -#include "petsird_helpers/geometry.h" #include "stir/listmode/CListRecordPETSIRD.h" diff --git a/src/include/stir/listmode/CListRecordPETSIRD.h b/src/include/stir/listmode/CListRecordPETSIRD.h index 4fd54d85fd..117c559aca 100644 --- a/src/include/stir/listmode/CListRecordPETSIRD.h +++ b/src/include/stir/listmode/CListRecordPETSIRD.h @@ -49,9 +49,6 @@ #include "stir/DetectorCoordinateMap.h" #include "boost/make_shared.hpp" -#include "petsird_helpers.h" -#include "petsird_helpers/create.h" -#include "petsird_helpers/geometry.h" START_NAMESPACE_STIR @@ -59,18 +56,6 @@ START_NAMESPACE_STIR Provides interface of the record class to STIR by implementing get_LOR(). It uses an optional map from detector indices to coordinates to specify LORAs2Points from given detection pair indices. -The record has the following format (for little-endian byte order) -\code - unsigned ringA : 8; - unsigned ringB : 8; - unsigned detA : 16; - unsigned detB : 16; - unsigned layerA : 4; - unsigned layerB : 4; - unsigned reserved : 6; - unsigned isDelayed : 1; - unsigned type : 1; -\endcode \ingroup listmode */ template @@ -156,51 +141,6 @@ class CListEventDataPETSIRD #endif }; -//! Class for record with coincidence data using NeuroLF bitfield definition -/*! \ingroup listmode */ -class CListEventDataNeuroLF -{ -public: - //! Writes detection position pair to reference given as argument. - inline void get_detection_position_pair(DetectionPositionPair<>& det_pos_pair); - - //! Returns 0 if event is prompt and 1 if delayed - inline bool is_prompt() const { return !isDelayed; } - - //! Returns 1 if if event is time and 0 if it is prompt - inline bool is_time() const { return type; } - - //! Can be used to set "promptness" of event. - inline Succeeded set_prompt(const bool prompt = true) - { - isDelayed = !prompt; - return Succeeded::yes; - } - -private: -#if STIRIsNativeByteOrderBigEndian - unsigned type : 1; - unsigned isDelayed : 1; - unsigned reserved : 8; - unsigned layerB : 3; - unsigned layerA : 3; - unsigned detB : 16; - unsigned detA : 16; - unsigned ringB : 8; - unsigned ringA : 8; -#else - unsigned ringA : 8; - unsigned ringB : 8; - unsigned detA : 16; - unsigned detB : 16; - unsigned layerA : 3; - unsigned layerB : 3; - unsigned reserved : 8; - unsigned isDelayed : 1; - unsigned type : 1; -#endif -}; - //! Class for record with time data using PETSIRD bitfield definition /*! \ingroup listmode */ class CListTimeDataPETSIRD @@ -215,15 +155,7 @@ class CListTimeDataPETSIRD inline bool is_time() const { return type; } private: -#if STIRIsNativeByteOrderBigEndian - boost::uint64_t type : 1; - boost::uint64_t reserved : 15; - boost::uint64_t time : 48; -#else - boost::uint64_t time : 48; - boost::uint64_t reserved : 15; - boost::uint64_t type : 1; -#endif + }; //! Class for general PETSIRD record, containing a union of data, time and raw record and providing access to certain elements. diff --git a/src/listmode_buildblock/CListModeDataPETSIRD.cxx b/src/listmode_buildblock/CListModeDataPETSIRD.cxx index 225332d5d7..80a9efc257 100644 --- a/src/listmode_buildblock/CListModeDataPETSIRD.cxx +++ b/src/listmode_buildblock/CListModeDataPETSIRD.cxx @@ -39,6 +39,8 @@ Coincidence LM Data Class for PETSIRD: Implementation #include "stir/Succeeded.h" #include "stir/info.h" #include "stir/error.h" +#include "binary/protocols.h" +#include "petsird_helpers.h" //#include "boost/static_assert.hpp" @@ -62,6 +64,12 @@ CListModeDataPETSIRD::CListModeDataPETSIRD(const std::string& list template_proj_data_filename, lor_randomization_sigma) { + petsird::Header header; + petsird::binary::PETSIRDReader petsird_reader(filename); + petsird_reader.ReadHeader(header); + ProjDataInfoGenericNoArcCorr projdata_info; + ExamInfo exam_info; + Scanner scanner_info(Scanner::Unknown_scanner); if (!crystal_map_filename.empty()) { this->map = MAKE_SHARED(crystal_map_filename, lor_randomization_sigma); @@ -75,7 +83,8 @@ CListModeDataPETSIRD::CListModeDataPETSIRD(const std::string& list _exam_info_sptr->imaging_modality = ImagingModality::PT; this->exam_info_sptr = _exam_info_sptr; - // Here we are reading the scanner data from the template projdata + // Here we are reading the scanner data from the template projdata from the petsird header + 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()); diff --git a/src/listmode_buildblock/CMakeLists.txt b/src/listmode_buildblock/CMakeLists.txt index 4497205b89..d893356520 100644 --- a/src/listmode_buildblock/CMakeLists.txt +++ b/src/listmode_buildblock/CMakeLists.txt @@ -70,3 +70,24 @@ if (HAVE_HDF5) # for GEHDF5, TODO remove once IO dependency added or GEHDF5Wrapper no longer includes H5Cpp.h target_include_directories(listmode_buildblock PRIVATE ${HDF5_INCLUDE_DIRS}) endif() + +if (HAVE_PETSIRD) +#set(PETSIRD_dir ../../PETSIRD/cpp/generated) +#add_subdirectory(${PETSIRD_dir} PETSIRD_generated) + +target_include_directories(IO PUBLIC + $ + $ +) + +target_include_directories(IO PUBLIC + $ + $ +) + +target_include_directories(IO PUBLIC + $ + $ +) +#target_link_libraries(IO PUBLIC petsird_generated) +endif() From 0f9deb217ac0e24aa2f0665869635d1be93f8a66 Mon Sep 17 00:00:00 2001 From: nmdicom-recon Date: Wed, 16 Jul 2025 09:58:27 +0100 Subject: [PATCH 10/42] correct cmake importing of sird into lmbuildblock. can import binary/ --- src/listmode_buildblock/CListModeDataPETSIRD.cxx | 4 +--- src/listmode_buildblock/CMakeLists.txt | 6 +++--- 2 files changed, 4 insertions(+), 6 deletions(-) diff --git a/src/listmode_buildblock/CListModeDataPETSIRD.cxx b/src/listmode_buildblock/CListModeDataPETSIRD.cxx index 80a9efc257..a4f0ea0a7f 100644 --- a/src/listmode_buildblock/CListModeDataPETSIRD.cxx +++ b/src/listmode_buildblock/CListModeDataPETSIRD.cxx @@ -40,8 +40,6 @@ Coincidence LM Data Class for PETSIRD: Implementation #include "stir/info.h" #include "stir/error.h" #include "binary/protocols.h" -#include "petsird_helpers.h" - //#include "boost/static_assert.hpp" #include "stir/listmode/CListModeDataPETSIRD.h" @@ -65,7 +63,7 @@ CListModeDataPETSIRD::CListModeDataPETSIRD(const std::string& list lor_randomization_sigma) { petsird::Header header; - petsird::binary::PETSIRDReader petsird_reader(filename); + petsird::binary::PETSIRDReader petsird_reader(listmode_filename); petsird_reader.ReadHeader(header); ProjDataInfoGenericNoArcCorr projdata_info; ExamInfo exam_info; diff --git a/src/listmode_buildblock/CMakeLists.txt b/src/listmode_buildblock/CMakeLists.txt index d893356520..433476fd0d 100644 --- a/src/listmode_buildblock/CMakeLists.txt +++ b/src/listmode_buildblock/CMakeLists.txt @@ -75,17 +75,17 @@ if (HAVE_PETSIRD) #set(PETSIRD_dir ../../PETSIRD/cpp/generated) #add_subdirectory(${PETSIRD_dir} PETSIRD_generated) -target_include_directories(IO PUBLIC +target_include_directories(listmode_buildblock PUBLIC $ $ ) -target_include_directories(IO PUBLIC +target_include_directories(listmode_buildblock PUBLIC $ $ ) -target_include_directories(IO PUBLIC +target_include_directories(listmode_buildblock PUBLIC $ $ ) From 3550178771d14a8a8eea038354703d4f34dcd919 Mon Sep 17 00:00:00 2001 From: danieldeidda Date: Wed, 16 Jul 2025 11:01:16 +0100 Subject: [PATCH 11/42] started reading info from header --- .../stir/listmode/CListRecordPETSIRD.inl | 11 ----------- .../CListModeDataPETSIRD.cxx | 17 ++++++++++++++--- 2 files changed, 14 insertions(+), 14 deletions(-) diff --git a/src/include/stir/listmode/CListRecordPETSIRD.inl b/src/include/stir/listmode/CListRecordPETSIRD.inl index 56b426aa8d..f60ec41881 100644 --- a/src/include/stir/listmode/CListRecordPETSIRD.inl +++ b/src/include/stir/listmode/CListRecordPETSIRD.inl @@ -132,17 +132,6 @@ CListEventDataPETSIRD::get_detection_position_pair(DetectionPositionPair<>& det_ det_pos_pair.pos2().tangential_coord() = detB; } -void -CListEventDataNeuroLF::get_detection_position_pair(DetectionPositionPair<>& det_pos_pair) -{ - det_pos_pair.pos1().radial_coord() = layerA; - det_pos_pair.pos2().radial_coord() = layerB; - - det_pos_pair.pos1().axial_coord() = ringA; - det_pos_pair.pos2().axial_coord() = ringB; - det_pos_pair.pos1().tangential_coord() = detA; - det_pos_pair.pos2().tangential_coord() = detB; -} END_NAMESPACE_STIR diff --git a/src/listmode_buildblock/CListModeDataPETSIRD.cxx b/src/listmode_buildblock/CListModeDataPETSIRD.cxx index a4f0ea0a7f..f135dfd986 100644 --- a/src/listmode_buildblock/CListModeDataPETSIRD.cxx +++ b/src/listmode_buildblock/CListModeDataPETSIRD.cxx @@ -40,6 +40,9 @@ Coincidence LM Data Class for PETSIRD: Implementation #include "stir/info.h" #include "stir/error.h" #include "binary/protocols.h" +#include "helpers/include/petsird_helpers.h" +#include "helpers/include/petsird_helpers/create.h" +#include "helpers/include/petsird_helpers/geometry.h" //#include "boost/static_assert.hpp" #include "stir/listmode/CListModeDataPETSIRD.h" @@ -65,9 +68,17 @@ CListModeDataPETSIRD::CListModeDataPETSIRD(const std::string& list petsird::Header header; petsird::binary::PETSIRDReader petsird_reader(listmode_filename); petsird_reader.ReadHeader(header); - ProjDataInfoGenericNoArcCorr projdata_info; - ExamInfo exam_info; - Scanner scanner_info(Scanner::Unknown_scanner); + petsird::ScannerInformation scanner_info=header.scanner; + petsird::ScannerGeometry scanner_geo=scanner_info.scanner_geometry; +// scanner_geo.replicated_modules; + + + +// ExamInfo exam_info; +// Scanner scanner_info(Scanner::Unknown_scanner); + + + if (!crystal_map_filename.empty()) { this->map = MAKE_SHARED(crystal_map_filename, lor_randomization_sigma); From 32daa97ef9b3613d4e34fe972d5c7fadbd836450 Mon Sep 17 00:00:00 2001 From: NikEfth Date: Wed, 16 Jul 2025 07:15:54 -0400 Subject: [PATCH 12/42] Work in separating the SAFIR from the BasedOnCoordinateMAP and PETSIRD listmode data classes. --- CMakeLists.txt | 91 +++++----- src/IO/CMakeLists.txt | 43 +++-- src/IO/IO_registries.cxx | 5 + src/cmake/STIRConfig.cmake.in | 4 + src/cmake/STIRConfig.h.in | 2 + .../stir/IO/PETSIRDCListmodeInputFileFormat.h | 116 +++++++----- .../CListModeDataBasedOnCoordinateMap.h | 73 +++++--- .../stir/listmode/CListModeDataPETSIRD.h | 25 ++- .../stir/listmode/CListModeDataSAFIR.h | 20 ++- .../stir/listmode/CListRecordPETSIRD.h | 169 +++++++----------- .../stir/listmode/CListRecordPETSIRD.inl | 143 +++++++-------- .../CListModeDataBasedOnCoordinateMap.cxx | 84 +-------- .../CListModeDataPETSIRD.cxx | 90 +++++----- .../CListModeDataSAFIR.cxx | 53 ++++-- 14 files changed, 444 insertions(+), 474 deletions(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index c2e9df9a29..9766e791fd 100755 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -261,49 +261,54 @@ else() endif() if(NOT DISABLE_PETSIRD) - if(GIT_FOUND AND EXISTS "${PROJECT_SOURCE_DIR}/.git") - # Check if PETSIRD is already registered as a submodule - execute_process( - COMMAND ${GIT_EXECUTABLE} config --file ${PROJECT_SOURCE_DIR}/.gitmodules --get-regexp "submodule\\.PETSIRD\\.path" - RESULT_VARIABLE SUBMOD_EXISTS - OUTPUT_QUIET - ERROR_QUIET - ) - - if(NOT SUBMOD_EXISTS EQUAL 0) - message(STATUS "Adding PETSIRD submodule...") - execute_process( - COMMAND ${GIT_EXECUTABLE} submodule add https://github.com/ETSInitiative/PETSIRD.git PETSIRD - WORKING_DIRECTORY ${PROJECT_SOURCE_DIR} - RESULT_VARIABLE GIT_ADD_RESULT - ) - if(NOT GIT_ADD_RESULT EQUAL 0) - message(WARNING "Submodule add failed with code ${GIT_ADD_RESULT}") - endif() - else() - message(STATUS "PETSIRD submodule already exists in .gitmodules.") - endif() - - # Always update/init to ensure it's ready - execute_process( - COMMAND ${GIT_EXECUTABLE} submodule update --init --recursive - WORKING_DIRECTORY ${PROJECT_SOURCE_DIR} - ) - endif() - - execute_process( - COMMAND just generate - WORKING_DIRECTORY ${PROJECT_SOURCE_DIR}/PETSIRD - RESULT_VARIABLE JUST_RESULT - ) - if(JUST_RESULT EQUAL 0) - set(HAVE_PETSIRD TRUE) - set(PETSIRD_base_dir ${PROJECT_SOURCE_DIR}/PETSIRD/cpp/generated) - message(STATUS "PETSIRD generation succeeded. THE PATH is ${PETSIRD_base_dir}. HAVE_PETSIRD set to TRUE.") - else() - set(HAVE_PETSIRD FALSE) - message(WARNING "PETSIRD generation failed with code ${JUST_RESULT}. HAVE_PETSIRD not set.") - endif() + if(GIT_FOUND AND EXISTS "${PROJECT_SOURCE_DIR}/.git") + # Check if PETSIRD is already registered as a submodule + execute_process( + COMMAND ${GIT_EXECUTABLE} config --file ${PROJECT_SOURCE_DIR}/.gitmodules --get-regexp "submodule\\.PETSIRD\\.path" + RESULT_VARIABLE SUBMOD_EXISTS + OUTPUT_QUIET + ERROR_QUIET + ) + + if(NOT SUBMOD_EXISTS EQUAL 0) + message(STATUS "Adding PETSIRD submodule...") + execute_process( + COMMAND ${GIT_EXECUTABLE} submodule add https://github.com/ETSInitiative/PETSIRD.git PETSIRD + WORKING_DIRECTORY ${PROJECT_SOURCE_DIR} + RESULT_VARIABLE GIT_ADD_RESULT + ) + if(NOT GIT_ADD_RESULT EQUAL 0) + message(WARNING "Submodule add failed with code ${GIT_ADD_RESULT}") + endif() + else() + message(STATUS "PETSIRD submodule already exists in .gitmodules.") + endif() + + # Always update/init to ensure it's ready + execute_process( + COMMAND ${GIT_EXECUTABLE} submodule update --init --recursive + WORKING_DIRECTORY ${PROJECT_SOURCE_DIR} + ) + endif() + + add_subdirectory(${PROJECT_SOURCE_DIR}/PETSIRD/cpp PETSIRD_generated) + + # execute_process( + # COMMAND just generate + # WORKING_DIRECTORY ${PROJECT_SOURCE_DIR}/PETSIRD + # RESULT_VARIABLE JUST_RESULT + # ) + # if(JUST_RESULT EQUAL 0) + set(HAVE_PETSIRD ON) + + + # set(PETSIRD_base_dir ${PROJECT_SOURCE_DIR}/PETSIRD/cpp/generated) + install(TARGETS petsird_generated EXPORT STIRTargets DESTINATION lib) + # message(STATUS "PETSIRD generation succeeded. THE PATH is ${PETSIRD_base_dir}. HAVE_PETSIRD set to TRUE.") + # else() + # set(HAVE_PETSIRD OFF) + # message(WARNING "PETSIRD generation failed with code ${JUST_RESULT}. HAVE_PETSIRD not set.") + # endif() endif() #### enable support for ctest diff --git a/src/IO/CMakeLists.txt b/src/IO/CMakeLists.txt index 6c70745e62..99d71be0a0 100644 --- a/src/IO/CMakeLists.txt +++ b/src/IO/CMakeLists.txt @@ -139,25 +139,30 @@ if (NOT MINI_STIR) endif() if (HAVE_PETSIRD) -#set(PETSIRD_dir ../../PETSIRD/cpp/generated) -#add_subdirectory(${PETSIRD_dir} PETSIRD_generated) - -target_include_directories(IO PUBLIC - $ - $ -) - -target_include_directories(IO PUBLIC - $ - $ -) - -target_include_directories(IO PUBLIC - $ - $ -) - -#target_link_libraries(IO PUBLIC petsird_generated) +# # #set(PETSIRD_dir ../../PETSIRD/cpp/generated) + +# # target_include_directories(IO PUBLIC +# # $ +# # $ +# # ) + +# # target_include_directories(IO PUBLIC +# # $ +# # $ +# # ) + +# # target_include_directories(IO PUBLIC +# # $ +# # $ +# # ) +# target_include_directories(IO PUBLIC +# $ +# $ +# $ +# $ +# ) + +# target_link_libraries(IO PUBLIC petsird_generated) endif() diff --git a/src/IO/IO_registries.cxx b/src/IO/IO_registries.cxx index 242a42113c..b739170860 100644 --- a/src/IO/IO_registries.cxx +++ b/src/IO/IO_registries.cxx @@ -156,5 +156,10 @@ static InputStreamWithRecordsFromUPENNtxt::RegisterIt dummy686062; // static RegisterInputFileFormat idummy1(2); # endif +# ifdef HAVE_PETSIRD +# include "stir/IO/PETSIRDCListmodeInputFileFormat.h" +static RegisterInputFileFormat<::stir::stir::PETSIRDCListmodeInputFileFormat> LMdummyPETSIRD(10); +#endif + #endif // MINI_STIR END_NAMESPACE_STIR diff --git a/src/cmake/STIRConfig.cmake.in b/src/cmake/STIRConfig.cmake.in index baff57b8c1..3ae838e78b 100644 --- a/src/cmake/STIRConfig.cmake.in +++ b/src/cmake/STIRConfig.cmake.in @@ -160,6 +160,10 @@ if(@STIR_WITH_Parallelproj_PROJECTOR@) set(STIR_WITH_Parallelproj_PROJECTOR TRUE) endif() +if(@HAVE_PETSIRD@) + set(HAVE_PETSIRD TRUE) +endif() + SET(STIR_WITH_EXPERIMENTAL @STIR_ENABLE_EXPERIMENTAL@) if(STIR_WITH_EXPERIMENTAL) if(${CMAKE_VERSION} VERSION_LESS "3.12.0") diff --git a/src/cmake/STIRConfig.h.in b/src/cmake/STIRConfig.h.in index efc241dae9..5055d63a31 100644 --- a/src/cmake/STIRConfig.h.in +++ b/src/cmake/STIRConfig.h.in @@ -90,6 +90,8 @@ namespace stir { #cmakedefine HAVE_SYSTEM_GETOPT +#cmakedefine HAVE_PETSIRD + #cmakedefine STIR_DEFAULT_PROJECTOR_AS_V2 #ifndef STIR_DEFAULT_PROJECTOR_AS_V2 #define USE_PMRT diff --git a/src/include/stir/IO/PETSIRDCListmodeInputFileFormat.h b/src/include/stir/IO/PETSIRDCListmodeInputFileFormat.h index e5e446ad2a..6258a58580 100644 --- a/src/include/stir/IO/PETSIRDCListmodeInputFileFormat.h +++ b/src/include/stir/IO/PETSIRDCListmodeInputFileFormat.h @@ -44,9 +44,25 @@ #include "stir/utilities.h" #include "stir/ParsingObject.h" -#include "stir/listmode/CListRecordPETSIRD.h" +// #include "stir/listmode/CListRecordPETSIRD.h" #include "stir/listmode/CListModeDataPETSIRD.h" +// #include "../../../../PETSIRD/cpp/generated/yardl/yardl.h" +// #include "../../PETSIRD/cpp/generated/types.h" +// #ifdef HAVE_HDF5 +// // # include "../../PETSIRD/cpp/generated/hdf5/protocols.h" +// // using petsird::hdf5::PETSIRDReader; +// #else +// // # include "../../PETSIRD/cpp/generated/binary/protocols.h" +// // using petsird::binary::PETSIRDReader; +// #endif + + +// #include "../../PETSIRD/cpp/helpers/include/petsird_helpers.h" + +// #include "types.h" +// #include "hdf5/protocols.h" + START_NAMESPACE_STIR /*! \brief Class for reading PETSIRD coincidence listmode data. @@ -76,7 +92,9 @@ each event. "PETSIRD CListModeData\0" and "NeuroLF CListModeData\0". If either is successfull, the class claims it can read the file format. The rest of the file is read as records as specified as template parameter, e.g. CListRecordPETSIRD. */ -template + +// using namespace petsird::binary; + class PETSIRDCListmodeInputFileFormat : public InputFileFormat, public ParsingObject { public: @@ -93,43 +111,47 @@ class PETSIRDCListmodeInputFileFormat : public InputFileFormat, pu //! CListModeData"). bool can_read(const FileSignature& signature, const std::string& filename) const override { - // Looking for the right key in the parameter file - std::ifstream par_file(filename.c_str()); - std::string key; - std::getline(par_file, key, ':'); - key = standardise_interfile_keyword(key); - if (key != std::string("clistmodedataPETSIRD parameters")) - { - return false; - } - if (!actual_do_parsing(filename)) - return false; - std::ifstream data_file(listmode_filename.c_str(), std::ios::binary); - char* buffer = new char[32]; - data_file.read(buffer, 32); - bool cr = false; - // depending on used template, check header of listmode file for correct format - if (std::is_same::value) - { - cr = (!strncmp(buffer, "MUPET CListModeData\0", 20) || !strncmp(buffer, "PETSIRD CListModeData\0", 20)); - } - else if (std::is_same::value) - { - cr = !strncmp(buffer, "NeuroLF CListModeData\0", 20); - } - else - { - warning("PETSIRDCListModeInputFileFormat was initialised with an unexpected template."); - } - - if (!cr) - { - warning("PETSIRDCListModeInputFileFormat tried to read file " + listmode_filename - + " but it seems to have the wrong signature."); - } - - delete[] buffer; - return cr; + int nikos = 0; + std::string d = filename; + // PETSIRDReader ndn(d); + // // Looking for the right key in the parameter file + // std::ifstream par_file(filename.c_str()); + // std::string key; + // std::getline(par_file, key, ':'); + // key = standardise_interfile_keyword(key); + // if (key != std::string("clistmodedataPETSIRD parameters")) + // { + // return false; + // } + // if (!actual_do_parsing(filename)) + // return false; + // std::ifstream data_file(listmode_filename.c_str(), std::ios::binary); + // char* buffer = new char[32]; + // data_file.read(buffer, 32); + // bool cr = false; + // // depending on used template, check header of listmode file for correct format + // if (std::is_same::value) + // { + // cr = (!strncmp(buffer, "MUPET CListModeData\0", 20) || !strncmp(buffer, "PETSIRD CListModeData\0", 20)); + // } + // else if (std::is_same::value) + // { + // cr = !strncmp(buffer, "NeuroLF CListModeData\0", 20); + // } + // else + // { + // warning("PETSIRDCListModeInputFileFormat was initialised with an unexpected template."); + // } + + // if (!cr) + // { + // warning("PETSIRDCListModeInputFileFormat tried to read file " + listmode_filename + // + " but it seems to have the wrong signature."); + // } + + // delete[] buffer; + // return cr; + return true; } std::unique_ptr read_from_file(std::istream& input) const override @@ -179,14 +201,14 @@ class PETSIRDCListmodeInputFileFormat : public InputFileFormat, pu bool actual_do_parsing(const std::string& filename) const { - // Ugly const_casts here, but I don't see an other nice way to use the parser - if (const_cast*>(this)->parse(filename.c_str())) - { - info(const_cast*>(this)->parameter_info()); - return true; - } - else - return false; + // // Ugly const_casts here, but I don't see an other nice way to use the parser + // if (const_cast*>(this)->parse(filename.c_str())) + // { + // info(const_cast*>(this)->parameter_info()); + // return true; + // } + // else + // return false; } bool post_processing() override diff --git a/src/include/stir/listmode/CListModeDataBasedOnCoordinateMap.h b/src/include/stir/listmode/CListModeDataBasedOnCoordinateMap.h index 2ed6f211e0..81ab98f515 100644 --- a/src/include/stir/listmode/CListModeDataBasedOnCoordinateMap.h +++ b/src/include/stir/listmode/CListModeDataBasedOnCoordinateMap.h @@ -32,9 +32,7 @@ #ifndef __stir_listmode_CListModeDataBasedOnCoordinateMap_H__ #define __stir_listmode_CListModeDataBasedOnCoordinateMap_H__ -#include #include -#include #include #include "stir/listmode/CListModeData.h" @@ -44,39 +42,24 @@ #include "stir/IO/InputStreamWithRecords.h" #include "stir/shared_ptr.h" -#include "stir/listmode/CListRecordSAFIR.h" +// #include "stir/listmode/CListRecordSAFIR.h" #include "stir/DetectorCoordinateMap.h" START_NAMESPACE_STIR -template class CListModeDataBasedOnCoordinateMap : public CListModeData { public: - /*! Constructor - \par - Takes as arguments the filenames of the coicidence listmode file, the crystal map (text) file, and the template projection data - file - */ - CListModeDataBasedOnCoordinateMap(const std::string& listmode_filename, - const std::string& crystal_map_filename, - const std::string& template_proj_data_filename, - const double lor_randomization_sigma = 0.0); - - CListModeDataBasedOnCoordinateMap(const std::string& listmode_filename, const shared_ptr& proj_data_info_sptr); std::string get_name() const override; shared_ptr get_empty_record_sptr() const override; Succeeded get_next_record(CListRecord& record_of_general_type) const override; Succeeded reset() override; - /*! - This function should save the position in input file. This is not implemented but disabled. - Returns 0 in the moement. - \todo Maybe provide real implementation? - */ - SavedPosition save_get_position() override { return static_cast(current_lm_data_ptr->save_get_position()); } - Succeeded set_get_position(const SavedPosition& pos) override { return current_lm_data_ptr->set_get_position(pos); } + virtual shared_ptr> get_current_lm_file() = 0; + + SavedPosition save_get_position() override { return static_cast(get_current_lm_file()->save_get_position()); } + Succeeded set_get_position(const SavedPosition& pos) override { return get_current_lm_file()->set_get_position(pos); } /*! Returns just false in the moment. @@ -86,7 +69,7 @@ class CListModeDataBasedOnCoordinateMap : public CListModeData protected: std::string listmode_filename; - mutable shared_ptr> current_lm_data_ptr; + mutable std::vector saved_get_positions; virtual Succeeded open_lm_file() const = 0; @@ -94,7 +77,49 @@ class CListModeDataBasedOnCoordinateMap : public CListModeData }; - +// CListModeDataBasedOnCoordinateMap::CListModeDataBasedOnCoordinateMap(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) +// { +// if (!crystal_map_filename.empty()) +// { +// map = MAKE_SHARED(crystal_map_filename, lor_randomization_sigma); +// } +// else +// { +// if (lor_randomization_sigma != 0) +// error("SAFIR currently does not support LOR-randomisation unless a map is specified"); +// } +// shared_ptr _exam_info_sptr(new ExamInfo); +// _exam_info_sptr->imaging_modality = ImagingModality::PT; +// this->exam_info_sptr = _exam_info_sptr; + +// // 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"); +// } +// } + +// // CListModeDataBasedOnCoordinateMap::CListModeDataBasedOnCoordinateMap(const std::string& listmode_filename, +// // const shared_ptr& proj_data_info_sptr) +// // : listmode_filename(listmode_filename) +// // { +// // shared_ptr _exam_info_sptr(new ExamInfo); +// // _exam_info_sptr->imaging_modality = ImagingModality::PT; +// // this->exam_info_sptr = _exam_info_sptr; +// // this->set_proj_data_info_sptr(proj_data_info_sptr->create_shared_clone()); + +// // if (open_lm_file() == Succeeded::no) +// // { +// // error("CListModeDataSAFIR: opening file \"" + listmode_filename + "\""); +// // } +// // } END_NAMESPACE_STIR diff --git a/src/include/stir/listmode/CListModeDataPETSIRD.h b/src/include/stir/listmode/CListModeDataPETSIRD.h index 70ad8d66ee..b92669f090 100644 --- a/src/include/stir/listmode/CListModeDataPETSIRD.h +++ b/src/include/stir/listmode/CListModeDataPETSIRD.h @@ -45,11 +45,12 @@ Jannis Fischer #include "stir/listmode/CListRecord.h" #include "stir/IO/InputStreamWithRecords.h" #include "stir/shared_ptr.h" -#include "petsird_helpers.h" -#include "petsird_helpers/create.h" -#include "petsird_helpers/geometry.h" -#include "stir/listmode/CListRecordPETSIRD.h" +// #include "../../PETSIRD/cpp/helpers/include/petsird_helpers.h" +// #include "petsird_helpers/create.h" +// #include "petsird_helpers/geometry.h" + +// #include "stir/listmode/CListRecordPETSIRD.h" START_NAMESPACE_STIR @@ -60,8 +61,8 @@ START_NAMESPACE_STIR By providing crystal map and template projection data files, the coordinates are read from files and used defining the LOR coordinates. */ -template -class CListModeDataPETSIRD : public CListModeDataBasedOnCoordinateMap + +class CListModeDataPETSIRD : public CListModeDataBasedOnCoordinateMap { public: @@ -70,8 +71,18 @@ class CListModeDataPETSIRD : public CListModeDataBasedOnCoordinateMap get_empty_record_sptr() const override { + + return nullptr; + } + + Succeeded get_next_record(CListRecord& record_of_general_type) const override{ + + return Succeeded::no; + } + protected: - virtual Succeeded open_lm_file() const; + virtual Succeeded open_lm_file() const override; }; diff --git a/src/include/stir/listmode/CListModeDataSAFIR.h b/src/include/stir/listmode/CListModeDataSAFIR.h index 6fcf424531..4f84d350b0 100644 --- a/src/include/stir/listmode/CListModeDataSAFIR.h +++ b/src/include/stir/listmode/CListModeDataSAFIR.h @@ -33,10 +33,7 @@ Jannis Fischer #define __stir_listmode_CListModeDataSAFIR_H__ -#include #include -#include -#include #include "stir/listmode/CListModeDataBasedOnCoordinateMap.h" #include "stir/ProjData.h" @@ -45,8 +42,6 @@ Jannis Fischer #include "stir/IO/InputStreamWithRecords.h" #include "stir/shared_ptr.h" -#include "stir/listmode/CListRecordSAFIR.h" - START_NAMESPACE_STIR /*! @@ -57,7 +52,7 @@ START_NAMESPACE_STIR coordinates. */ template -class CListModeDataSAFIR : public CListModeDataBasedOnCoordinateMap +class CListModeDataSAFIR : public CListModeDataBasedOnCoordinateMap { public: @@ -66,9 +61,18 @@ class CListModeDataSAFIR : public CListModeDataBasedOnCoordinateMap& proj_data_info_sptr); + + shared_ptr get_empty_record_sptr() const override; + Succeeded get_next_record(CListRecord& record_of_general_type) const override; + virtual shared_ptr> get_current_lm_file(){ + return current_lm_data_ptr; + }; + +protected: + virtual Succeeded open_lm_file() const override; + mutable shared_ptr> current_lm_data_ptr; }; END_NAMESPACE_STIR diff --git a/src/include/stir/listmode/CListRecordPETSIRD.h b/src/include/stir/listmode/CListRecordPETSIRD.h index 4fd54d85fd..249dd7f700 100644 --- a/src/include/stir/listmode/CListRecordPETSIRD.h +++ b/src/include/stir/listmode/CListRecordPETSIRD.h @@ -49,9 +49,9 @@ #include "stir/DetectorCoordinateMap.h" #include "boost/make_shared.hpp" -#include "petsird_helpers.h" -#include "petsird_helpers/create.h" -#include "petsird_helpers/geometry.h" +// #include "petsird_helpers.h" +// #include "petsird_helpers/create.h" +// #include "petsird_helpers/geometry.h" START_NAMESPACE_STIR @@ -73,7 +73,7 @@ The record has the following format (for little-endian byte order) \endcode \ingroup listmode */ -template + class CListEventPETSIRD : public CListEvent { public: @@ -96,7 +96,7 @@ class CListEventPETSIRD : public CListEvent inline bool is_valid_template(const ProjDataInfo&) const override { return true; } //! Returns 0 if event is prompt and 1 if delayed - inline bool is_prompt() const override { return !(static_cast(this)->is_prompt()); } + inline bool is_prompt() const override { return true; }//!(static_cast(this)->is_prompt()); } //! Function to set map for detector indices to coordinates. /*! Use a null pointer to disable the mapping functionality */ inline void set_map_sptr(shared_ptr new_map_sptr) { map_sptr = new_map_sptr; } @@ -111,95 +111,51 @@ class CListEventPETSIRD : public CListEvent const DetectorCoordinateMap& map_to_use() const { return map_sptr ? *map_sptr : *this->scanner_sptr->get_detector_map_sptr(); } }; -//! Class for record with coincidence data using PETSIRD bitfield definition -/*! \ingroup listmode */ -class CListEventDataPETSIRD -{ -public: - //! Writes detection position pair to reference given as argument. - inline void get_detection_position_pair(DetectionPositionPair<>& det_pos_pair); - - //! Returns 0 if event is prompt and 1 if delayed - inline bool is_prompt() const { return !isDelayed; } +// //! Class for record with coincidence data using PETSIRD bitfield definition +// /*! \ingroup listmode */ +// class CListEventDataPETSIRD +// { +// public: +// //! Writes detection position pair to reference given as argument. +// inline void get_detection_position_pair(DetectionPositionPair<>& det_pos_pair); + +// //! Returns 0 if event is prompt and 1 if delayed +// inline bool is_prompt() const { return !isDelayed; } + +// //! Returns 1 if if event is time and 0 if it is prompt +// inline bool is_time() const { return type; } + +// //! Can be used to set "promptness" of event. +// inline Succeeded set_prompt(const bool prompt = true) +// { +// isDelayed = !prompt; +// return Succeeded::yes; +// } + +// private: +// #if STIRIsNativeByteOrderBigEndian +// unsigned type : 1; +// unsigned isDelayed : 1; +// unsigned reserved : 6; +// unsigned layerB : 4; +// unsigned layerA : 4; +// unsigned detB : 16; +// unsigned detA : 16; +// unsigned ringB : 8; +// unsigned ringA : 8; +// #else +// unsigned ringA : 8; +// unsigned ringB : 8; +// unsigned detA : 16; +// unsigned detB : 16; +// unsigned layerA : 4; +// unsigned layerB : 4; +// unsigned reserved : 6; +// unsigned isDelayed : 1; +// unsigned type : 1; +// #endif +// }; - //! Returns 1 if if event is time and 0 if it is prompt - inline bool is_time() const { return type; } - - //! Can be used to set "promptness" of event. - inline Succeeded set_prompt(const bool prompt = true) - { - isDelayed = !prompt; - return Succeeded::yes; - } - -private: -#if STIRIsNativeByteOrderBigEndian - unsigned type : 1; - unsigned isDelayed : 1; - unsigned reserved : 6; - unsigned layerB : 4; - unsigned layerA : 4; - unsigned detB : 16; - unsigned detA : 16; - unsigned ringB : 8; - unsigned ringA : 8; -#else - unsigned ringA : 8; - unsigned ringB : 8; - unsigned detA : 16; - unsigned detB : 16; - unsigned layerA : 4; - unsigned layerB : 4; - unsigned reserved : 6; - unsigned isDelayed : 1; - unsigned type : 1; -#endif -}; - -//! Class for record with coincidence data using NeuroLF bitfield definition -/*! \ingroup listmode */ -class CListEventDataNeuroLF -{ -public: - //! Writes detection position pair to reference given as argument. - inline void get_detection_position_pair(DetectionPositionPair<>& det_pos_pair); - - //! Returns 0 if event is prompt and 1 if delayed - inline bool is_prompt() const { return !isDelayed; } - - //! Returns 1 if if event is time and 0 if it is prompt - inline bool is_time() const { return type; } - - //! Can be used to set "promptness" of event. - inline Succeeded set_prompt(const bool prompt = true) - { - isDelayed = !prompt; - return Succeeded::yes; - } - -private: -#if STIRIsNativeByteOrderBigEndian - unsigned type : 1; - unsigned isDelayed : 1; - unsigned reserved : 8; - unsigned layerB : 3; - unsigned layerA : 3; - unsigned detB : 16; - unsigned detA : 16; - unsigned ringB : 8; - unsigned ringA : 8; -#else - unsigned ringA : 8; - unsigned ringB : 8; - unsigned detA : 16; - unsigned detB : 16; - unsigned layerA : 3; - unsigned layerB : 3; - unsigned reserved : 8; - unsigned isDelayed : 1; - unsigned type : 1; -#endif -}; //! Class for record with time data using PETSIRD bitfield definition /*! \ingroup listmode */ @@ -226,18 +182,16 @@ class CListTimeDataPETSIRD #endif }; -//! Class for general PETSIRD record, containing a union of data, time and raw record and providing access to certain elements. -/*! \ingroup listmode */ -template -class CListRecordPETSIRD : public CListRecord, public ListTime, public CListEventPETSIRD> + +class CListRecordPETSIRD : public CListRecord, public ListTime, public CListEventPETSIRD { public: //! Returns event_data (without checking if the type is really event and not time). - DataType get_data() const { return this->event_data; } + CListEventPETSIRD get_data() const { return this->event_data; } - CListRecordPETSIRD() - : CListEventPETSIRD>() - {} + // CListRecordPETSIRD() + // : CListEventPETSIRD>() + // {} ~CListRecordPETSIRD() override {} @@ -249,9 +203,9 @@ class CListRecordPETSIRD : public CListRecord, public ListTime, public CListEven const CListEvent& event() const override { return *this; } - virtual CListEventPETSIRD>& event_PETSIRD() { return *this; } + virtual CListEventPETSIRD& event_PETSIRD() { return *this; } - virtual const CListEventPETSIRD>& event_PETSIRD() const { return *this; } + virtual const CListEventPETSIRD& event_PETSIRD() const { return *this; } ListTime& time() override { return *this; } @@ -259,8 +213,8 @@ class CListRecordPETSIRD : public CListRecord, public ListTime, public CListEven virtual bool operator==(const CListRecord& e2) const { - return dynamic_cast const*>(&e2) != 0 - && raw == static_cast const&>(e2).raw; + return dynamic_cast(&e2) != 0 + && raw == static_cast(e2).raw; } inline unsigned long get_time_in_millisecs() const override { return time_data.get_time_in_millisecs(); } @@ -293,13 +247,10 @@ class CListRecordPETSIRD : public CListRecord, public ListTime, public CListEven // However, this is used as a feature if comparing events over the 'raw' type. union { - DataType event_data; + CListEventPETSIRD event_data; CListTimeDataPETSIRD time_data; boost::int64_t raw; }; - BOOST_STATIC_ASSERT(sizeof(boost::uint64_t) == 8); - BOOST_STATIC_ASSERT(sizeof(DataType) == 8); - BOOST_STATIC_ASSERT(sizeof(CListTimeDataPETSIRD) == 8); }; END_NAMESPACE_STIR diff --git a/src/include/stir/listmode/CListRecordPETSIRD.inl b/src/include/stir/listmode/CListRecordPETSIRD.inl index 56b426aa8d..086c23d958 100644 --- a/src/include/stir/listmode/CListRecordPETSIRD.inl +++ b/src/include/stir/listmode/CListRecordPETSIRD.inl @@ -51,14 +51,13 @@ START_NAMESPACE_STIR -template LORAs2Points -CListEventPETSIRD::get_LOR() const +CListEventPETSIRD::get_LOR() const { LORAs2Points lor; DetectionPositionPair<> det_pos_pair; - static_cast(this)->get_data().get_detection_position_pair(det_pos_pair); + // static_cast(this)->get_data().get_detection_position_pair(det_pos_pair); lor.p1() = map_to_use().get_coordinate_for_index(det_pos_pair.pos1()); lor.p2() = map_to_use().get_coordinate_for_index(det_pos_pair.pos2()); @@ -68,81 +67,69 @@ CListEventPETSIRD::get_LOR() const namespace detail { -template -static inline bool -get_bin_for_det_pos_pair(Bin& bin, DetectionPositionPair<>& det_pos_pair, const ProjDataInfo& proj_data_info) -{ - if (auto proj_data_info_ptr = dynamic_cast(&proj_data_info)) - { - if (proj_data_info_ptr->get_bin_for_det_pos_pair(bin, det_pos_pair) == Succeeded::yes) - bin.set_bin_value(1); - else - bin.set_bin_value(-1); - return true; - } - else - return false; -} -} // namespace detail - -template -void -CListEventPETSIRD::get_bin(Bin& bin, const ProjDataInfo& proj_data_info) const -{ - DetectionPositionPair<> det_pos_pair; - static_cast(this)->get_data().get_detection_position_pair(det_pos_pair); - - if (!map_sptr) - { - // transform det_pos_pair into stir conventions - det_pos_pair.pos1() = map_to_use().get_det_pos_for_index(det_pos_pair.pos1()); - det_pos_pair.pos2() = map_to_use().get_det_pos_for_index(det_pos_pair.pos2()); - - if (det_pos_pair.pos1().tangential_coord() == det_pos_pair.pos2().tangential_coord()) - { - bin.set_bin_value(-1); - return; - } - - if (!detail::get_bin_for_det_pos_pair(bin, det_pos_pair, proj_data_info)) - { - if (!detail::get_bin_for_det_pos_pair(bin, det_pos_pair, proj_data_info)) - error("Wrong type of proj-data-info for PETSIRD"); - } - } - else - { - const stir::CartesianCoordinate3D c1 = map_sptr->get_coordinate_for_index(det_pos_pair.pos1()); - const stir::CartesianCoordinate3D c2 = map_sptr->get_coordinate_for_index(det_pos_pair.pos2()); - const LORAs2Points lor(c1, c2); - bin = proj_data_info.get_bin(lor); - } -} - -void -CListEventDataPETSIRD::get_detection_position_pair(DetectionPositionPair<>& det_pos_pair) -{ - det_pos_pair.pos1().radial_coord() = layerA; - det_pos_pair.pos2().radial_coord() = layerB; +// template +// static inline bool +// get_bin_for_det_pos_pair(Bin& bin, DetectionPositionPair<>& det_pos_pair, const ProjDataInfo& proj_data_info) +// { +// if (auto proj_data_info_ptr = dynamic_cast(&proj_data_info)) +// { +// if (proj_data_info_ptr->get_bin_for_det_pos_pair(bin, det_pos_pair) == Succeeded::yes) +// bin.set_bin_value(1); +// else +// bin.set_bin_value(-1); +// return true; +// } +// else +// return false; +// } +// } // namespace detail + +// template +// void +// CListEventPETSIRD::get_bin(Bin& bin, const ProjDataInfo& proj_data_info) const +// { +// DetectionPositionPair<> det_pos_pair; +// static_cast(this)->get_data().get_detection_position_pair(det_pos_pair); + +// if (!map_sptr) +// { +// // transform det_pos_pair into stir conventions +// det_pos_pair.pos1() = map_to_use().get_det_pos_for_index(det_pos_pair.pos1()); +// det_pos_pair.pos2() = map_to_use().get_det_pos_for_index(det_pos_pair.pos2()); + +// if (det_pos_pair.pos1().tangential_coord() == det_pos_pair.pos2().tangential_coord()) +// { +// bin.set_bin_value(-1); +// return; +// } + +// if (!detail::get_bin_for_det_pos_pair(bin, det_pos_pair, proj_data_info)) +// { +// if (!detail::get_bin_for_det_pos_pair(bin, det_pos_pair, proj_data_info)) +// error("Wrong type of proj-data-info for PETSIRD"); +// } +// } +// else +// { +// const stir::CartesianCoordinate3D c1 = map_sptr->get_coordinate_for_index(det_pos_pair.pos1()); +// const stir::CartesianCoordinate3D c2 = map_sptr->get_coordinate_for_index(det_pos_pair.pos2()); +// const LORAs2Points lor(c1, c2); +// bin = proj_data_info.get_bin(lor); +// } +// } + +// void +// CListEventDataPETSIRD::get_detection_position_pair(DetectionPositionPair<>& det_pos_pair) +// { +// det_pos_pair.pos1().radial_coord() = layerA; +// det_pos_pair.pos2().radial_coord() = layerB; + +// det_pos_pair.pos1().axial_coord() = ringA; +// det_pos_pair.pos2().axial_coord() = ringB; + +// det_pos_pair.pos1().tangential_coord() = detA; +// det_pos_pair.pos2().tangential_coord() = detB; +// } - det_pos_pair.pos1().axial_coord() = ringA; - det_pos_pair.pos2().axial_coord() = ringB; - - det_pos_pair.pos1().tangential_coord() = detA; - det_pos_pair.pos2().tangential_coord() = detB; -} - -void -CListEventDataNeuroLF::get_detection_position_pair(DetectionPositionPair<>& det_pos_pair) -{ - det_pos_pair.pos1().radial_coord() = layerA; - det_pos_pair.pos2().radial_coord() = layerB; - - det_pos_pair.pos1().axial_coord() = ringA; - det_pos_pair.pos2().axial_coord() = ringB; - - det_pos_pair.pos1().tangential_coord() = detA; - det_pos_pair.pos2().tangential_coord() = detB; -} END_NAMESPACE_STIR diff --git a/src/listmode_buildblock/CListModeDataBasedOnCoordinateMap.cxx b/src/listmode_buildblock/CListModeDataBasedOnCoordinateMap.cxx index 439b9a8dd2..8f3564563a 100644 --- a/src/listmode_buildblock/CListModeDataBasedOnCoordinateMap.cxx +++ b/src/listmode_buildblock/CListModeDataBasedOnCoordinateMap.cxx @@ -30,17 +30,9 @@ Coincidence LM Data Class for SAFIR: Implementation */ #include #include -#include - -#include "stir/ExamInfo.h" #include "stir/Succeeded.h" -#include "stir/info.h" -#include "stir/error.h" - -//#include "boost/static_assert.hpp" #include "stir/listmode/CListModeDataBasedOnCoordinateMap.h" -#include "stir/listmode/CListRecordSAFIR.h" using std::ios; using std::fstream; @@ -49,87 +41,17 @@ using std::istream; START_NAMESPACE_STIR; -template -CListModeDataBasedOnCoordinateMap::CListModeDataBasedOnCoordinateMap(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) -{ - if (!crystal_map_filename.empty()) - { - map = MAKE_SHARED(crystal_map_filename, lor_randomization_sigma); - } - else - { - if (lor_randomization_sigma != 0) - error("SAFIR currently does not support LOR-randomisation unless a map is specified"); - } - shared_ptr _exam_info_sptr(new ExamInfo); - _exam_info_sptr->imaging_modality = ImagingModality::PT; - this->exam_info_sptr = _exam_info_sptr; - - // 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 -CListModeDataBasedOnCoordinateMap::CListModeDataBasedOnCoordinateMap(const std::string& listmode_filename, - const shared_ptr& proj_data_info_sptr) - : listmode_filename(listmode_filename) -{ - shared_ptr _exam_info_sptr(new ExamInfo); - _exam_info_sptr->imaging_modality = ImagingModality::PT; - this->exam_info_sptr = _exam_info_sptr; - this->set_proj_data_info_sptr(proj_data_info_sptr->create_shared_clone()); - - if (open_lm_file() == Succeeded::no) - { - error("CListModeDataSAFIR: opening file \"" + listmode_filename + "\""); - } -} - -template std::string -CListModeDataBasedOnCoordinateMap::get_name() const +CListModeDataBasedOnCoordinateMap::get_name() const { return listmode_filename; } -template -shared_ptr -CListModeDataBasedOnCoordinateMap::get_empty_record_sptr() const -{ - shared_ptr sptr(new CListRecordT); - sptr->event_SAFIR().set_scanner_sptr(this->get_proj_data_info_sptr()->get_scanner_sptr()); - sptr->event_SAFIR().set_map_sptr(map); - return static_pointer_cast(sptr); -} -template Succeeded -CListModeDataBasedOnCoordinateMap::get_next_record(CListRecord& record_of_general_type) const +CListModeDataBasedOnCoordinateMap::reset() { - 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_sptr(map); - return status; + return get_current_lm_file()->reset(); } -template -Succeeded -CListModeDataBasedOnCoordinateMap::reset() -{ - return current_lm_data_ptr->reset(); -} - -// template class CListModeDataSAFIR>; -// template class CListModeDataSAFIR>; - END_NAMESPACE_STIR diff --git a/src/listmode_buildblock/CListModeDataPETSIRD.cxx b/src/listmode_buildblock/CListModeDataPETSIRD.cxx index 225332d5d7..242c9078f0 100644 --- a/src/listmode_buildblock/CListModeDataPETSIRD.cxx +++ b/src/listmode_buildblock/CListModeDataPETSIRD.cxx @@ -33,7 +33,6 @@ Coincidence LM Data Class for PETSIRD: Implementation */ #include #include -#include #include "stir/ExamInfo.h" #include "stir/Succeeded.h" @@ -43,7 +42,7 @@ Coincidence LM Data Class for PETSIRD: Implementation //#include "boost/static_assert.hpp" #include "stir/listmode/CListModeDataPETSIRD.h" -#include "stir/listmode/CListRecordPETSIRD.h" +// #include "stir/listmode/CListRecordPETSIRD.h" using std::ios; using std::fstream; @@ -52,58 +51,53 @@ using std::istream; START_NAMESPACE_STIR; -template -CListModeDataPETSIRD::CListModeDataPETSIRD(const std::string& listmode_filename, - const std::string& crystal_map_filename, - const std::string& template_proj_data_filename, - const double lor_randomization_sigma) - : CListModeDataBasedOnCoordinateMap(listmode_filename, - crystal_map_filename, - template_proj_data_filename, - lor_randomization_sigma) +CListModeDataPETSIRD::CListModeDataPETSIRD(const std::string& listmode_filename, + const std::string& crystal_map_filename, + const std::string& template_proj_data_filename, + const double lor_randomization_sigma) { - if (!crystal_map_filename.empty()) - { - this->map = MAKE_SHARED(crystal_map_filename, lor_randomization_sigma); - } - else - { - if (lor_randomization_sigma != 0) - error("PETSIRD currently does not support LOR-randomisation unless a map is specified"); - } - shared_ptr _exam_info_sptr(new ExamInfo); - _exam_info_sptr->imaging_modality = ImagingModality::PT; - this->exam_info_sptr = _exam_info_sptr; - - // 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 (this->open_lm_file() == Succeeded::no) - { - error("CListModeDataPETSIRD: Could not open listmode file " + listmode_filename + "\n"); - } + CListModeDataBasedOnCoordinateMap::listmode_filename = listmode_filename; + // if (!crystal_map_filename.empty()) + // { + // this->map = MAKE_SHARED(crystal_map_filename, lor_randomization_sigma); + // } + // else + // { + // if (lor_randomization_sigma != 0) + // error("PETSIRD currently does not support LOR-randomisation unless a map is specified"); + // } + // shared_ptr _exam_info_sptr(new ExamInfo); + // _exam_info_sptr->imaging_modality = ImagingModality::PT; + // this->exam_info_sptr = _exam_info_sptr; + + // // 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 (this->open_lm_file() == Succeeded::no) + // { + // error("CListModeDataPETSIRD: Could not open listmode file " + listmode_filename + "\n"); + // } } -template Succeeded -CListModeDataPETSIRD::open_lm_file() const +CListModeDataPETSIRD::open_lm_file() const { - shared_ptr stream_ptr(new fstream(this->listmode_filename.c_str(), ios::in | ios::binary)); - if (!(*stream_ptr)) - { - return Succeeded::no; - } - info("CListModeDataPETSIRD: opening file \"" + this->listmode_filename + "\"", 2); - stream_ptr->seekg((std::streamoff)32); - this->current_lm_data_ptr.reset( - new InputStreamWithRecords(stream_ptr, - sizeof(CListTimeDataPETSIRD), - sizeof(CListTimeDataPETSIRD), - ByteOrder::little_endian != ByteOrder::get_native_order())); - return Succeeded::yes; + // shared_ptr stream_ptr(new fstream(this->listmode_filename.c_str(), ios::in | ios::binary)); + // if (!(*stream_ptr)) + // { + // return Succeeded::no; + // } + // info("CListModeDataPETSIRD: opening file \"" + this->listmode_filename + "\"", 2); + // stream_ptr->seekg((std::streamoff)32); + // this->current_lm_data_ptr.reset( + // new InputStreamWithRecords(stream_ptr, + // sizeof(CListTimeDataPETSIRD), + // sizeof(CListTimeDataPETSIRD), + // ByteOrder::little_endian != ByteOrder::get_native_order())); + // return Succeeded::yes; } -template class CListModeDataPETSIRD>; +// template class CListModeDataPETSIRD; END_NAMESPACE_STIR diff --git a/src/listmode_buildblock/CListModeDataSAFIR.cxx b/src/listmode_buildblock/CListModeDataSAFIR.cxx index 79a6cbc10d..ea0497ae43 100644 --- a/src/listmode_buildblock/CListModeDataSAFIR.cxx +++ b/src/listmode_buildblock/CListModeDataSAFIR.cxx @@ -30,7 +30,6 @@ Coincidence LM Data Class for SAFIR: Implementation */ #include #include -#include #include "stir/ExamInfo.h" #include "stir/Succeeded.h" @@ -51,14 +50,11 @@ 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) - : CListModeDataBasedOnCoordinateMap(listmode_filename, - crystal_map_filename, - template_proj_data_filename, - lor_randomization_sigma) + const std::string& crystal_map_filename, + const std::string& template_proj_data_filename, + const double lor_randomization_sigma) { + CListModeDataBasedOnCoordinateMap::listmode_filename = listmode_filename; if (!crystal_map_filename.empty()) { this->map = MAKE_SHARED(crystal_map_filename, lor_randomization_sigma); @@ -82,6 +78,22 @@ CListModeDataSAFIR::CListModeDataSAFIR(const std::string& listmode } } +template +CListModeDataSAFIR::CListModeDataSAFIR(const std::string& listmode_filename, + const shared_ptr& proj_data_info_sptr) +{ + CListModeDataBasedOnCoordinateMap::listmode_filename = listmode_filename; + shared_ptr _exam_info_sptr(new ExamInfo); + _exam_info_sptr->imaging_modality = ImagingModality::PT; + this->exam_info_sptr = _exam_info_sptr; + this->set_proj_data_info_sptr(proj_data_info_sptr->create_shared_clone()); + + if (open_lm_file() == Succeeded::no) + { + error("CListModeDataSAFIR: opening file \"" + listmode_filename + "\""); + } +} + template Succeeded CListModeDataSAFIR::open_lm_file() const @@ -101,7 +113,28 @@ CListModeDataSAFIR::open_lm_file() const return Succeeded::yes; } -template class CListModeDataSAFIR>; -template class CListModeDataSAFIR>; +template +shared_ptr +CListModeDataSAFIR::get_empty_record_sptr() const +{ + shared_ptr sptr(new CListRecordT); + sptr->event_SAFIR().set_scanner_sptr(this->get_proj_data_info_sptr()->get_scanner_sptr()); + sptr->event_SAFIR().set_map_sptr(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_sptr(map); + return status; +} + +// template class CListModeDataSAFIR>; +// template class CListModeDataSAFIR>; END_NAMESPACE_STIR From ba44a959094420825fd76ecf0bdc4d9dd380f1f5 Mon Sep 17 00:00:00 2001 From: NikEfth Date: Wed, 16 Jul 2025 11:03:03 -0400 Subject: [PATCH 13/42] Important fix in CListModeDataPETSIRD.inl --- src/include/stir/listmode/CListRecordPETSIRD.inl | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/include/stir/listmode/CListRecordPETSIRD.inl b/src/include/stir/listmode/CListRecordPETSIRD.inl index 086c23d958..a500ccc628 100644 --- a/src/include/stir/listmode/CListRecordPETSIRD.inl +++ b/src/include/stir/listmode/CListRecordPETSIRD.inl @@ -131,5 +131,5 @@ namespace detail // det_pos_pair.pos2().tangential_coord() = detB; // } - +} // namespace detail END_NAMESPACE_STIR From 0ff78b1ddabe4963b92abcab7031d2152332ece7 Mon Sep 17 00:00:00 2001 From: NikEfth Date: Wed, 16 Jul 2025 11:46:47 -0400 Subject: [PATCH 14/42] * Fixes and interface clean up --- CMakeLists.txt | 87 ++++++------- src/IO/IO_registries.cxx | 8 +- .../stir/IO/PETSIRDCListmodeInputFileFormat.h | 118 +----------------- .../CListModeDataBasedOnCoordinateMap.h | 12 +- .../stir/listmode/CListModeDataPETSIRD.h | 25 ++-- .../stir/listmode/CListModeDataSAFIR.h | 12 +- .../stir/listmode/CListRecordPETSIRD.h | 58 ++++----- .../CListModeDataPETSIRD.cxx | 10 +- .../CListModeDataSAFIR.cxx | 9 +- 9 files changed, 98 insertions(+), 241 deletions(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index 9766e791fd..284a2cfa56 100755 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -261,54 +261,49 @@ else() endif() if(NOT DISABLE_PETSIRD) - if(GIT_FOUND AND EXISTS "${PROJECT_SOURCE_DIR}/.git") - # Check if PETSIRD is already registered as a submodule - execute_process( - COMMAND ${GIT_EXECUTABLE} config --file ${PROJECT_SOURCE_DIR}/.gitmodules --get-regexp "submodule\\.PETSIRD\\.path" - RESULT_VARIABLE SUBMOD_EXISTS - OUTPUT_QUIET - ERROR_QUIET - ) - - if(NOT SUBMOD_EXISTS EQUAL 0) - message(STATUS "Adding PETSIRD submodule...") - execute_process( - COMMAND ${GIT_EXECUTABLE} submodule add https://github.com/ETSInitiative/PETSIRD.git PETSIRD - WORKING_DIRECTORY ${PROJECT_SOURCE_DIR} - RESULT_VARIABLE GIT_ADD_RESULT - ) - if(NOT GIT_ADD_RESULT EQUAL 0) - message(WARNING "Submodule add failed with code ${GIT_ADD_RESULT}") - endif() - else() - message(STATUS "PETSIRD submodule already exists in .gitmodules.") - endif() - - # Always update/init to ensure it's ready - execute_process( - COMMAND ${GIT_EXECUTABLE} submodule update --init --recursive - WORKING_DIRECTORY ${PROJECT_SOURCE_DIR} - ) - endif() - - add_subdirectory(${PROJECT_SOURCE_DIR}/PETSIRD/cpp PETSIRD_generated) - - # execute_process( - # COMMAND just generate - # WORKING_DIRECTORY ${PROJECT_SOURCE_DIR}/PETSIRD - # RESULT_VARIABLE JUST_RESULT - # ) - # if(JUST_RESULT EQUAL 0) - set(HAVE_PETSIRD ON) + if(GIT_FOUND AND EXISTS "${PROJECT_SOURCE_DIR}/.git") + # Check if PETSIRD is already registered as a submodule + execute_process( + COMMAND ${GIT_EXECUTABLE} config --file ${PROJECT_SOURCE_DIR}/.gitmodules --get-regexp "submodule\\.PETSIRD\\.path" + RESULT_VARIABLE SUBMOD_EXISTS + OUTPUT_QUIET + ERROR_QUIET + ) + + if(NOT SUBMOD_EXISTS EQUAL 0) + message(STATUS "Adding PETSIRD submodule...") + execute_process( + COMMAND ${GIT_EXECUTABLE} submodule add https://github.com/ETSInitiative/PETSIRD.git PETSIRD + WORKING_DIRECTORY ${PROJECT_SOURCE_DIR} + RESULT_VARIABLE GIT_ADD_RESULT + ) + if(NOT GIT_ADD_RESULT EQUAL 0) + message(WARNING "Submodule add failed with code ${GIT_ADD_RESULT}") + endif() + else() + message(STATUS "PETSIRD submodule already exists in .gitmodules.") + endif() + # Always update/init to ensure it's ready + execute_process( + COMMAND ${GIT_EXECUTABLE} submodule update --init --recursive + WORKING_DIRECTORY ${PROJECT_SOURCE_DIR} + ) + endif() - # set(PETSIRD_base_dir ${PROJECT_SOURCE_DIR}/PETSIRD/cpp/generated) - install(TARGETS petsird_generated EXPORT STIRTargets DESTINATION lib) - # message(STATUS "PETSIRD generation succeeded. THE PATH is ${PETSIRD_base_dir}. HAVE_PETSIRD set to TRUE.") - # else() - # set(HAVE_PETSIRD OFF) - # message(WARNING "PETSIRD generation failed with code ${JUST_RESULT}. HAVE_PETSIRD not set.") - # endif() + execute_process( + COMMAND just generate + WORKING_DIRECTORY ${PROJECT_SOURCE_DIR}/PETSIRD + RESULT_VARIABLE JUST_RESULT + ) + if(JUST_RESULT EQUAL 0) + set(HAVE_PETSIRD TRUE) + set(PETSIRD_base_dir ${PROJECT_SOURCE_DIR}/PETSIRD/cpp/generated) + message(STATUS "PETSIRD generation succeeded. THE PATH is ${PETSIRD_base_dir}. HAVE_PETSIRD set to TRUE.") + else() + set(HAVE_PETSIRD FALSE) + message(WARNING "PETSIRD generation failed with code ${JUST_RESULT}. HAVE_PETSIRD not set.") + endif() endif() #### enable support for ctest diff --git a/src/IO/IO_registries.cxx b/src/IO/IO_registries.cxx index b739170860..0c6c46c258 100644 --- a/src/IO/IO_registries.cxx +++ b/src/IO/IO_registries.cxx @@ -156,10 +156,10 @@ static InputStreamWithRecordsFromUPENNtxt::RegisterIt dummy686062; // static RegisterInputFileFormat idummy1(2); # endif -# ifdef HAVE_PETSIRD -# include "stir/IO/PETSIRDCListmodeInputFileFormat.h" -static RegisterInputFileFormat<::stir::stir::PETSIRDCListmodeInputFileFormat> LMdummyPETSIRD(10); -#endif +# ifdef HAVE_PETSIRD +# include "stir/IO/PETSIRDCListmodeInputFileFormat.h" +static RegisterInputFileFormat LMdummyPETSIRD(10); +# endif #endif // MINI_STIR END_NAMESPACE_STIR diff --git a/src/include/stir/IO/PETSIRDCListmodeInputFileFormat.h b/src/include/stir/IO/PETSIRDCListmodeInputFileFormat.h index 1ccc4371a0..37891254d9 100644 --- a/src/include/stir/IO/PETSIRDCListmodeInputFileFormat.h +++ b/src/include/stir/IO/PETSIRDCListmodeInputFileFormat.h @@ -34,7 +34,6 @@ #ifndef __stir_IO_PETSIRDCListmodeInputFileFormat_H__ #define __stir_IO_PETSIRDCListmodeInputFileFormat_H__ -#include #include #include #include @@ -43,31 +42,11 @@ #include "stir/IO/InputFileFormat.h" #include "stir/IO/InputFileFormat.h" -#include "stir/IO/interfile.h" -#include "stir/info.h" #include "stir/error.h" -#include "stir/utilities.h" -#include "stir/ParsingObject.h" // #include "stir/listmode/CListRecordPETSIRD.h" #include "stir/listmode/CListModeDataPETSIRD.h" -// #include "../../../../PETSIRD/cpp/generated/yardl/yardl.h" -// #include "../../PETSIRD/cpp/generated/types.h" -// #ifdef HAVE_HDF5 -// // # include "../../PETSIRD/cpp/generated/hdf5/protocols.h" -// // using petsird::hdf5::PETSIRDReader; -// #else -// // # include "../../PETSIRD/cpp/generated/binary/protocols.h" -// // using petsird::binary::PETSIRDReader; -// #endif - - -// #include "../../PETSIRD/cpp/helpers/include/petsird_helpers.h" - -// #include "types.h" -// #include "hdf5/protocols.h" - START_NAMESPACE_STIR /*! \brief Class for reading PETSIRD coincidence listmode data. @@ -77,9 +56,7 @@ START_NAMESPACE_STIR rest of the file is read as records, e.g. CListRecordPETSIRD. */ -// using namespace petsird::binary; - -class PETSIRDCListmodeInputFileFormat : public InputFileFormat, public ParsingObject +class PETSIRDCListmodeInputFileFormat : public InputFileFormat { public: PETSIRDCListmodeInputFileFormat() {} @@ -144,98 +121,7 @@ class PETSIRDCListmodeInputFileFormat : public InputFileFormat, pu return unique_ptr(); } -// std::unique_ptr read_from_file(const std::string& filename) const override -// { - - -// // info("PETSIRDCListmodeInputFileFormat: read_from_file(" + std::string(filename) + ")"); -// // actual_do_parsing(filename); -// // return std::unique_ptr(new CListModeDataPETSIRD>( -// // listmode_filename, crystal_map_filename, template_proj_data_filename, lor_randomization_sigma)); -// } -protected: - - virtual bool - actual_can_read(const FileSignature& signature, - std::istream &input) const - { - if(!is_interfile_signature(signature.get_signature())) - return false; - else - { - const std::string signature_as_string(signature.get_signature(), signature.size()); - return signature_as_string.find("PETSIRD") != std::string::npos; - } - } - - - virtual unique_ptr read_from_file( - const std::string& filename) const - { - return unique_ptr(new CListModeDataPETSIRD(filename)); - } - - typedef ParsingObject base_type; - mutable std::string listmode_filename; - mutable std::string crystal_map_filename; - mutable std::string template_proj_data_filename; - mutable double lor_randomization_sigma; - -// bool actual_can_read(const FileSignature& signature, std::istream& input) const override -// { -// return false; // cannot read from istream -// } - - void initialise_keymap() override - { - base_type::initialise_keymap(); - this->parser.add_start_key("CListModeDataPETSIRD Parameters"); - this->parser.add_key("listmode data filename", &listmode_filename); - this->parser.add_key("crystal map filename", &crystal_map_filename); - this->parser.add_key("template projection data filename", &template_proj_data_filename); - this->parser.add_key("LOR randomization (Gaussian) sigma", &lor_randomization_sigma); - this->parser.add_stop_key("END CListModeDataPETSIRD Parameters"); - } - - void set_defaults() override - { - base_type::set_defaults(); - crystal_map_filename = ""; - template_proj_data_filename = ""; - lor_randomization_sigma = 0.0; - } - - bool actual_do_parsing(const std::string& filename) const - { - // // Ugly const_casts here, but I don't see an other nice way to use the parser - // if (const_cast*>(this)->parse(filename.c_str())) - // { - // info(const_cast*>(this)->parameter_info()); - // return true; - // } - // else - // return false; - } - - bool post_processing() override - { - if (!file_exists(listmode_filename)) - return true; - else if (!file_exists(template_proj_data_filename)) - return true; - else - { - return false; - } - return true; - } - -private: - bool file_exists(const std::string& filename) - { - std::ifstream infile(filename.c_str()); - return infile.good(); - } + std::unique_ptr read_from_file(const std::string& filename) const override {} }; END_NAMESPACE_STIR #endif diff --git a/src/include/stir/listmode/CListModeDataBasedOnCoordinateMap.h b/src/include/stir/listmode/CListModeDataBasedOnCoordinateMap.h index 81ab98f515..ee32535398 100644 --- a/src/include/stir/listmode/CListModeDataBasedOnCoordinateMap.h +++ b/src/include/stir/listmode/CListModeDataBasedOnCoordinateMap.h @@ -50,10 +50,9 @@ START_NAMESPACE_STIR class CListModeDataBasedOnCoordinateMap : public CListModeData { public: - std::string get_name() const override; - shared_ptr get_empty_record_sptr() const override; - Succeeded get_next_record(CListRecord& record_of_general_type) const override; + // shared_ptr get_empty_record_sptr() const override; + // Succeeded get_next_record(CListRecord& record_of_general_type) const override; Succeeded reset() override; virtual shared_ptr> get_current_lm_file() = 0; @@ -61,12 +60,6 @@ class CListModeDataBasedOnCoordinateMap : public CListModeData SavedPosition save_get_position() override { return static_cast(get_current_lm_file()->save_get_position()); } Succeeded set_get_position(const SavedPosition& pos) override { return get_current_lm_file()->set_get_position(pos); } - /*! - Returns just false in the moment. - \todo Implement this properly to check for delayed events in LM files. - */ - bool has_delayeds() const override { return false; } - protected: std::string listmode_filename; @@ -76,7 +69,6 @@ class CListModeDataBasedOnCoordinateMap : public CListModeData shared_ptr map; }; - // CListModeDataBasedOnCoordinateMap::CListModeDataBasedOnCoordinateMap(const std::string& listmode_filename, // const std::string& crystal_map_filename, // const std::string& template_proj_data_filename, diff --git a/src/include/stir/listmode/CListModeDataPETSIRD.h b/src/include/stir/listmode/CListModeDataPETSIRD.h index b92669f090..7cf2e78313 100644 --- a/src/include/stir/listmode/CListModeDataPETSIRD.h +++ b/src/include/stir/listmode/CListModeDataPETSIRD.h @@ -33,7 +33,6 @@ Jannis Fischer #ifndef __stir_listmode_CListModeDataPETSIRD_H__ #define __stir_listmode_CListModeDataPETSIRD_H__ - #include #include #include @@ -46,11 +45,7 @@ Jannis Fischer #include "stir/IO/InputStreamWithRecords.h" #include "stir/shared_ptr.h" -// #include "../../PETSIRD/cpp/helpers/include/petsird_helpers.h" -// #include "petsird_helpers/create.h" -// #include "petsird_helpers/geometry.h" - -// #include "stir/listmode/CListRecordPETSIRD.h" +#include "stir/listmode/CListRecordPETSIRD.h" START_NAMESPACE_STIR @@ -65,25 +60,21 @@ START_NAMESPACE_STIR class CListModeDataPETSIRD : public CListModeDataBasedOnCoordinateMap { public: - CListModeDataPETSIRD(const std::string& listmode_filename, - const std::string& crystal_map_filename, - const std::string& template_proj_data_filename, - const double lor_randomization_sigma = 0.0); + const std::string& crystal_map_filename, + const std::string& template_proj_data_filename, + const double lor_randomization_sigma = 0.0); - shared_ptr get_empty_record_sptr() const override { + shared_ptr get_empty_record_sptr() const override { return nullptr; } - return nullptr; - } + Succeeded get_next_record(CListRecord& record_of_general_type) const override { return Succeeded::no; } - Succeeded get_next_record(CListRecord& record_of_general_type) const override{ + virtual shared_ptr> get_current_lm_file() override {} - return Succeeded::no; - } + bool has_delayeds() const override { return false; } protected: virtual Succeeded open_lm_file() const override; - }; END_NAMESPACE_STIR diff --git a/src/include/stir/listmode/CListModeDataSAFIR.h b/src/include/stir/listmode/CListModeDataSAFIR.h index 4f84d350b0..c26d2798b0 100644 --- a/src/include/stir/listmode/CListModeDataSAFIR.h +++ b/src/include/stir/listmode/CListModeDataSAFIR.h @@ -32,7 +32,6 @@ Jannis Fischer #ifndef __stir_listmode_CListModeDataSAFIR_H__ #define __stir_listmode_CListModeDataSAFIR_H__ - #include #include "stir/listmode/CListModeDataBasedOnCoordinateMap.h" @@ -55,7 +54,6 @@ template class CListModeDataSAFIR : public CListModeDataBasedOnCoordinateMap { public: - CListModeDataSAFIR(const std::string& listmode_filename, const std::string& crystal_map_filename, const std::string& template_proj_data_filename, @@ -63,12 +61,12 @@ class CListModeDataSAFIR : public CListModeDataBasedOnCoordinateMap CListModeDataSAFIR(const std::string& listmode_filename, const shared_ptr& proj_data_info_sptr); - shared_ptr get_empty_record_sptr() const override; - Succeeded get_next_record(CListRecord& record_of_general_type) const override; + shared_ptr get_empty_record_sptr() const override; + Succeeded get_next_record(CListRecordT& record_of_general_type) const override; + + virtual shared_ptr> get_current_lm_file() { return current_lm_data_ptr; }; - virtual shared_ptr> get_current_lm_file(){ - return current_lm_data_ptr; - }; + bool has_delayeds() const override { return false; } protected: virtual Succeeded open_lm_file() const override; diff --git a/src/include/stir/listmode/CListRecordPETSIRD.h b/src/include/stir/listmode/CListRecordPETSIRD.h index 4a0d1b77d9..e05d020851 100644 --- a/src/include/stir/listmode/CListRecordPETSIRD.h +++ b/src/include/stir/listmode/CListRecordPETSIRD.h @@ -1,36 +1,36 @@ /* CListRecordPETSIRD.h - Coincidence Event Class for PETSIRD: Header File +Coincidence Event Class for PETSIRD: Header File - Copyright 2015 ETH Zurich, Institute of Particle Physics - Copyright 2017 ETH Zurich, Institute of Particle Physics and Astrophysics - Copyright 2020, 2022 Positrigo AG, Zurich - Copyright 2025 National Physical Laboratory + Copyright 2015 ETH Zurich, Institute of Particle Physics + Copyright 2017 ETH Zurich, Institute of Particle Physics and Astrophysics + Copyright 2020, 2022 Positrigo AG, Zurich + Copyright 2025 National Physical Laboratory - 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. */ /*! - \file - \ingroup listmode - \brief Declaration of class stir::CListEventPETSIRD and stir::CListRecordPETSIRD with supporting classes +\file +\ingroup listmode +\brief Declaration of class stir::CListEventPETSIRD and stir::CListRecordPETSIRD with supporting classes - \author Jannis Fischer - \author Parisa Khateri - \author Markus Jehl - \author Daniel Deidda +\author Jannis Fischer +\author Parisa Khateri +\author Markus Jehl +\author Daniel Deidda */ #ifndef __stir_listmode_CListRecordPETSIRD_H__ @@ -56,7 +56,7 @@ START_NAMESPACE_STIR Provides interface of the record class to STIR by implementing get_LOR(). It uses an optional map from detector indices to coordinates to specify LORAs2Points from given detection pair indices. - \ingroup listmode +\ingroup listmode */ class CListEventPETSIRD : public CListEvent @@ -81,7 +81,7 @@ class CListEventPETSIRD : public CListEvent inline bool is_valid_template(const ProjDataInfo&) const override { return true; } //! Returns 0 if event is prompt and 1 if delayed - inline bool is_prompt() const override { return true; }//!(static_cast(this)->is_prompt()); } + inline bool is_prompt() const override { return true; } //!(static_cast(this)->is_prompt()); } //! Function to set map for detector indices to coordinates. /*! Use a null pointer to disable the mapping functionality */ inline void set_map_sptr(shared_ptr new_map_sptr) { map_sptr = new_map_sptr; } @@ -141,23 +141,20 @@ class CListEventPETSIRD : public CListEvent // #endif // }; - //! Class for record with time data using PETSIRD bitfield definition /*! \ingroup listmode */ class CListTimeDataPETSIRD { public: - inline unsigned long get_time_in_millisecs() const { return static_cast(time); } + inline unsigned long get_time_in_millisecs() const { /*return static_cast(time);*/ } inline Succeeded set_time_in_millisecs(const unsigned long time_in_millisecs) { - time = ((boost::uint64_t(1) << 49) - 1) & static_cast(time_in_millisecs); + // time = ((boost::uint64_t(1) << 49) - 1) & static_cast(time_in_millisecs); return Succeeded::yes; } - inline bool is_time() const { return type; } - + inline bool is_time() const { /*return type; */ } }; - class CListRecordPETSIRD : public CListRecord, public ListTime, public CListEventPETSIRD { public: @@ -188,8 +185,7 @@ class CListRecordPETSIRD : public CListRecord, public ListTime, public CListEven virtual bool operator==(const CListRecord& e2) const { - return dynamic_cast(&e2) != 0 - && raw == static_cast(e2).raw; + return dynamic_cast(&e2) != 0 && raw == static_cast(e2).raw; } inline unsigned long get_time_in_millisecs() const override { return time_data.get_time_in_millisecs(); } diff --git a/src/listmode_buildblock/CListModeDataPETSIRD.cxx b/src/listmode_buildblock/CListModeDataPETSIRD.cxx index 0d258c1950..017b4a988a 100644 --- a/src/listmode_buildblock/CListModeDataPETSIRD.cxx +++ b/src/listmode_buildblock/CListModeDataPETSIRD.cxx @@ -39,10 +39,10 @@ Coincidence LM Data Class for PETSIRD: Implementation #include "stir/info.h" #include "stir/error.h" #include "binary/protocols.h" -#include "helpers/include/petsird_helpers.h" -#include "helpers/include/petsird_helpers/create.h" -#include "helpers/include/petsird_helpers/geometry.h" -//#include "boost/static_assert.hpp" +// #include "helpers/include/petsird_helpers.h" +// #include "helpers/include/petsird_helpers/create.h" +// #include "helpers/include/petsird_helpers/geometry.h" +// #include "boost/static_assert.hpp" #include "stir/listmode/CListModeDataPETSIRD.h" // #include "stir/listmode/CListRecordPETSIRD.h" @@ -52,7 +52,7 @@ using std::fstream; using std::ifstream; using std::istream; -START_NAMESPACE_STIR; +START_NAMESPACE_STIR CListModeDataPETSIRD::CListModeDataPETSIRD(const std::string& listmode_filename, const std::string& crystal_map_filename, diff --git a/src/listmode_buildblock/CListModeDataSAFIR.cxx b/src/listmode_buildblock/CListModeDataSAFIR.cxx index ea0497ae43..7d1a0dc5e5 100644 --- a/src/listmode_buildblock/CListModeDataSAFIR.cxx +++ b/src/listmode_buildblock/CListModeDataSAFIR.cxx @@ -36,7 +36,7 @@ Coincidence LM Data Class for SAFIR: Implementation #include "stir/info.h" #include "stir/error.h" -//#include "boost/static_assert.hpp" +// #include "boost/static_assert.hpp" #include "stir/listmode/CListModeDataSAFIR.h" #include "stir/listmode/CListRecordSAFIR.h" @@ -68,7 +68,7 @@ CListModeDataSAFIR::CListModeDataSAFIR(const std::string& listmode _exam_info_sptr->imaging_modality = ImagingModality::PT; this->exam_info_sptr = _exam_info_sptr; - // Here we are reading the scanner data from the template projdata + // 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()); @@ -114,7 +114,7 @@ CListModeDataSAFIR::open_lm_file() const } template -shared_ptr +shared_ptr CListModeDataSAFIR::get_empty_record_sptr() const { shared_ptr sptr(new CListRecordT); @@ -123,10 +123,9 @@ CListModeDataSAFIR::get_empty_record_sptr() const return static_pointer_cast(sptr); } - template Succeeded -CListModeDataSAFIR::get_next_record(CListRecord& record_of_general_type) const +CListModeDataSAFIR::get_next_record(CListRecordT& record_of_general_type) const { CListRecordT& record = static_cast(record_of_general_type); Succeeded status = current_lm_data_ptr->get_next_record(record); From f6de2516ff2fc375b5f97eb0e68e8df2eca329f6 Mon Sep 17 00:00:00 2001 From: NikEfth Date: Wed, 16 Jul 2025 12:44:16 -0400 Subject: [PATCH 15/42] Removed constructor. --- src/include/stir/IO/PETSIRDCListmodeInputFileFormat.h | 1 - 1 file changed, 1 deletion(-) diff --git a/src/include/stir/IO/PETSIRDCListmodeInputFileFormat.h b/src/include/stir/IO/PETSIRDCListmodeInputFileFormat.h index 37891254d9..cc27dd2e81 100644 --- a/src/include/stir/IO/PETSIRDCListmodeInputFileFormat.h +++ b/src/include/stir/IO/PETSIRDCListmodeInputFileFormat.h @@ -59,7 +59,6 @@ rest of the file is read as records, e.g. CListRecordPETSIRD. class PETSIRDCListmodeInputFileFormat : public InputFileFormat { public: - PETSIRDCListmodeInputFileFormat() {} const std::string get_name() const override { return "PETSIRD"; } //! Checks in binary data file for correct signature. From 43febf164532ca429c4e70e232f50766126a4660 Mon Sep 17 00:00:00 2001 From: danieldeidda Date: Wed, 16 Jul 2025 17:52:58 +0100 Subject: [PATCH 16/42] reading header and extracting scanner info --- src/listmode_buildblock/CListModeDataPETSIRD.cxx | 13 +++++++++++++ 1 file changed, 13 insertions(+) diff --git a/src/listmode_buildblock/CListModeDataPETSIRD.cxx b/src/listmode_buildblock/CListModeDataPETSIRD.cxx index 017b4a988a..ce183d3301 100644 --- a/src/listmode_buildblock/CListModeDataPETSIRD.cxx +++ b/src/listmode_buildblock/CListModeDataPETSIRD.cxx @@ -60,6 +60,19 @@ CListModeDataPETSIRD::CListModeDataPETSIRD(const std::string& listmode_filename, const double lor_randomization_sigma) { CListModeDataBasedOnCoordinateMap::listmode_filename = listmode_filename; + petsird::Header header; + petsird::binary::PETSIRDReader petsird_reader(listmode_filename); + petsird_reader.ReadHeader(header); + petsird::ScannerInformation scanner_info = header.scanner; + petsird::ScannerGeometry scanner_geo = scanner_info.scanner_geometry; + // need to get inner_ring_radius, num_detector_layers, num_transaxial_crystals_per_block, num_axial_crystals_per_block + // transaxial_crystal_spacing,average_depth_of_interaction,axial_crystal_spacing, num_rings, ring_spacing, num_axial_blocks, + // num_oftransaxial_blocks + // these are from rep_module.object + + std::vector replicated_module_list = scanner_geo.replicated_modules; + // petsird::ReplicatedDetectorModule = scanner_geo.replicated_modules; + // if (!crystal_map_filename.empty()) // { // this->map = MAKE_SHARED(crystal_map_filename, lor_randomization_sigma); From 12e6f57256f89f80d5f25bcadccf75a0cc5eb7d1 Mon Sep 17 00:00:00 2001 From: NikEfth Date: Wed, 16 Jul 2025 12:56:23 -0400 Subject: [PATCH 17/42] Fixed registries! :) --- src/IO/IO_registries.cxx | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/src/IO/IO_registries.cxx b/src/IO/IO_registries.cxx index 0c6c46c258..814b7e875b 100644 --- a/src/IO/IO_registries.cxx +++ b/src/IO/IO_registries.cxx @@ -66,6 +66,10 @@ # include "stir/IO/InputStreamFromROOTFileForECATPET.h" # endif +# ifdef HAVE_PETSIRD +# include "stir/IO/PETSIRDCListmodeInputFileFormat.h" +# endif + # ifdef HAVE_UPENN # include "stir/IO/PENNListmodeInputFileFormat.h" # include "stir/IO/InputStreamWithRecordsFromUPENNbin.h" @@ -157,7 +161,6 @@ static InputStreamWithRecordsFromUPENNtxt::RegisterIt dummy686062; # endif # ifdef HAVE_PETSIRD -# include "stir/IO/PETSIRDCListmodeInputFileFormat.h" static RegisterInputFileFormat LMdummyPETSIRD(10); # endif From 48972220ca6e02096f9b370417534ba192e10cee Mon Sep 17 00:00:00 2001 From: NikEfth Date: Wed, 16 Jul 2025 16:25:43 -0400 Subject: [PATCH 18/42] Updates on the interface and CMake --- CMakeLists.txt | 3 + src/IO/CMakeLists.txt | 20 ++++-- src/IO/PETSIRDCListmodeInputFileFormat.cxx | 26 +++++++ .../stir/IO/PETSIRDCListmodeInputFileFormat.h | 68 ++++--------------- .../stir/listmode/CListModeDataPETSIRD.h | 4 ++ .../stir/listmode/CListRecordPETSIRD.inl | 67 ------------------ .../CListModeDataPETSIRD.cxx | 8 +-- src/listmode_buildblock/CMakeLists.txt | 32 +++++---- 8 files changed, 81 insertions(+), 147 deletions(-) create mode 100644 src/IO/PETSIRDCListmodeInputFileFormat.cxx diff --git a/CMakeLists.txt b/CMakeLists.txt index 284a2cfa56..d590e607af 100755 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -296,6 +296,9 @@ if(NOT DISABLE_PETSIRD) WORKING_DIRECTORY ${PROJECT_SOURCE_DIR}/PETSIRD RESULT_VARIABLE JUST_RESULT ) + + # set(PETSIRD_dir ../../PETSIRD/cpp/generated) + add_subdirectory(${PROJECT_SOURCE_DIR}/PETSIRD/cpp petsird_generated) if(JUST_RESULT EQUAL 0) set(HAVE_PETSIRD TRUE) set(PETSIRD_base_dir ${PROJECT_SOURCE_DIR}/PETSIRD/cpp/generated) diff --git a/src/IO/CMakeLists.txt b/src/IO/CMakeLists.txt index 99d71be0a0..e05c516018 100644 --- a/src/IO/CMakeLists.txt +++ b/src/IO/CMakeLists.txt @@ -79,6 +79,12 @@ if (HAVE_HDF5) ) endif() +if (HAVE_PETSIRD) +list(APPEND ${dir_LIB_SOURCES} +PETSIRDCListmodeInputFileFormat.cxx +) +endif() + endif() # MINI_STIR include(stir_lib_target) @@ -139,12 +145,16 @@ if (NOT MINI_STIR) endif() if (HAVE_PETSIRD) -# # #set(PETSIRD_dir ../../PETSIRD/cpp/generated) + #set(PETSIRD_dir ../../PETSIRD/cpp/generated) + + +target_include_directories(IO PUBLIC + $ + $ + $ + $ +) -# # target_include_directories(IO PUBLIC -# # $ -# # $ -# # ) # # target_include_directories(IO PUBLIC # # $ diff --git a/src/IO/PETSIRDCListmodeInputFileFormat.cxx b/src/IO/PETSIRDCListmodeInputFileFormat.cxx new file mode 100644 index 0000000000..52fe2a869d --- /dev/null +++ b/src/IO/PETSIRDCListmodeInputFileFormat.cxx @@ -0,0 +1,26 @@ +#include "stir/IO/PETSIRDCListmodeInputFileFormat.h" +#include "../../PETSIRD/cpp/generated/binary/protocols.h" +// #include "../../PETSIRD/cpp/generated/types.h" +// #include "../../PETSIRD/cpp/helpers/include/petsird_helpers.h" + +START_NAMESPACE_STIR + +bool +PETSIRDCListmodeInputFileFormat::can_read(const FileSignature& signature, const std::string& filename) const +{ + + int nikos = 0; + nikos += 40; + + if (nikos == 40) + std::cout << filename << std::endl; + + petsird::Header header; + // petsird::binary::PETSIRDReader petsird_reader(filename); + // petsird_reader.ReadHeader(header); + // petsird::ScannerInformation scanner_info = header.scanner; + // petsird::ScannerGeometry scanner_geo = scanner_info.scanner_geometry; + return true; // cannot read from istream +} + +END_NAMESPACE_STIR diff --git a/src/include/stir/IO/PETSIRDCListmodeInputFileFormat.h b/src/include/stir/IO/PETSIRDCListmodeInputFileFormat.h index cc27dd2e81..1caffe327b 100644 --- a/src/include/stir/IO/PETSIRDCListmodeInputFileFormat.h +++ b/src/include/stir/IO/PETSIRDCListmodeInputFileFormat.h @@ -34,17 +34,11 @@ #ifndef __stir_IO_PETSIRDCListmodeInputFileFormat_H__ #define __stir_IO_PETSIRDCListmodeInputFileFormat_H__ -#include -#include -#include +// #include "boost/algorithm/string.hpp" -#include "boost/algorithm/string.hpp" - -#include "stir/IO/InputFileFormat.h" #include "stir/IO/InputFileFormat.h" #include "stir/error.h" -// #include "stir/listmode/CListRecordPETSIRD.h" #include "stir/listmode/CListModeDataPETSIRD.h" START_NAMESPACE_STIR @@ -62,65 +56,27 @@ class PETSIRDCListmodeInputFileFormat : public InputFileFormat const std::string get_name() const override { return "PETSIRD"; } //! Checks in binary data file for correct signature. - bool can_read(const FileSignature& signature, std::istream& input) const override - { - return false; // cannot read from istream - } + bool can_read(const FileSignature& signature, const std::string& filename) const override; - //! Checks in binary data file for correct signature (can be either "PETSIRD CListModeData", "NeuroLF CListModeData" or "MUPET - //! CListModeData"). - bool can_read(const FileSignature& signature, const std::string& filename) const override +protected: + bool actual_can_read(const FileSignature& signature, std::istream& input) const override { int nikos = 0; - std::string d = filename; - // PETSIRDReader ndn(d); - // // Looking for the right key in the parameter file - // std::ifstream par_file(filename.c_str()); - // std::string key; - // std::getline(par_file, key, ':'); - // key = standardise_interfile_keyword(key); - // if (key != std::string("clistmodedataPETSIRD parameters")) - // { - // return false; - // } - // if (!actual_do_parsing(filename)) - // return false; - // std::ifstream data_file(listmode_filename.c_str(), std::ios::binary); - // char* buffer = new char[32]; - // data_file.read(buffer, 32); - // bool cr = false; - // // depending on used template, check header of listmode file for correct format - // if (std::is_same::value) - // { - // cr = (!strncmp(buffer, "MUPET CListModeData\0", 20) || !strncmp(buffer, "PETSIRD CListModeData\0", 20)); - // } - // else if (std::is_same::value) - // { - // cr = !strncmp(buffer, "NeuroLF CListModeData\0", 20); - // } - // else - // { - // warning("PETSIRDCListModeInputFileFormat was initialised with an unexpected template."); - // } - - // if (!cr) - // { - // warning("PETSIRDCListModeInputFileFormat tried to read file " + listmode_filename - // + " but it seems to have the wrong signature."); - // } - - // delete[] buffer; - // return cr; return true; } - std::unique_ptr read_from_file(std::istream& input) const override +public: + unique_ptr read_from_file(std::istream& input) const override { - error("read_from_file for PETSIRDCListmodeData with istream not implemented %s:%d. Sorry", __FILE__, __LINE__); + error("read_from_file for ROOT listmode data with istream not implemented %s:%s. Sorry", __FILE__, __LINE__); return unique_ptr(); } - std::unique_ptr read_from_file(const std::string& filename) const override {} + unique_ptr read_from_file(const std::string& filename) const override + { + int nikos = 0; + // return unique_ptr(new CListModeDataPETSIRD(filename)); + } }; END_NAMESPACE_STIR #endif diff --git a/src/include/stir/listmode/CListModeDataPETSIRD.h b/src/include/stir/listmode/CListModeDataPETSIRD.h index 7cf2e78313..3701793a58 100644 --- a/src/include/stir/listmode/CListModeDataPETSIRD.h +++ b/src/include/stir/listmode/CListModeDataPETSIRD.h @@ -60,6 +60,10 @@ START_NAMESPACE_STIR class CListModeDataPETSIRD : public CListModeDataBasedOnCoordinateMap { public: + CListModeDataPETSIRD(const std::string& listmode_filename) + { + CListModeDataBasedOnCoordinateMap::listmode_filename = listmode_filename; + } CListModeDataPETSIRD(const std::string& listmode_filename, const std::string& crystal_map_filename, const std::string& template_proj_data_filename, diff --git a/src/include/stir/listmode/CListRecordPETSIRD.inl b/src/include/stir/listmode/CListRecordPETSIRD.inl index a500ccc628..6f4dbdbc2f 100644 --- a/src/include/stir/listmode/CListRecordPETSIRD.inl +++ b/src/include/stir/listmode/CListRecordPETSIRD.inl @@ -65,71 +65,4 @@ CListEventPETSIRD::get_LOR() const return lor; } -namespace detail -{ -// template -// static inline bool -// get_bin_for_det_pos_pair(Bin& bin, DetectionPositionPair<>& det_pos_pair, const ProjDataInfo& proj_data_info) -// { -// if (auto proj_data_info_ptr = dynamic_cast(&proj_data_info)) -// { -// if (proj_data_info_ptr->get_bin_for_det_pos_pair(bin, det_pos_pair) == Succeeded::yes) -// bin.set_bin_value(1); -// else -// bin.set_bin_value(-1); -// return true; -// } -// else -// return false; -// } -// } // namespace detail - -// template -// void -// CListEventPETSIRD::get_bin(Bin& bin, const ProjDataInfo& proj_data_info) const -// { -// DetectionPositionPair<> det_pos_pair; -// static_cast(this)->get_data().get_detection_position_pair(det_pos_pair); - -// if (!map_sptr) -// { -// // transform det_pos_pair into stir conventions -// det_pos_pair.pos1() = map_to_use().get_det_pos_for_index(det_pos_pair.pos1()); -// det_pos_pair.pos2() = map_to_use().get_det_pos_for_index(det_pos_pair.pos2()); - -// if (det_pos_pair.pos1().tangential_coord() == det_pos_pair.pos2().tangential_coord()) -// { -// bin.set_bin_value(-1); -// return; -// } - -// if (!detail::get_bin_for_det_pos_pair(bin, det_pos_pair, proj_data_info)) -// { -// if (!detail::get_bin_for_det_pos_pair(bin, det_pos_pair, proj_data_info)) -// error("Wrong type of proj-data-info for PETSIRD"); -// } -// } -// else -// { -// const stir::CartesianCoordinate3D c1 = map_sptr->get_coordinate_for_index(det_pos_pair.pos1()); -// const stir::CartesianCoordinate3D c2 = map_sptr->get_coordinate_for_index(det_pos_pair.pos2()); -// const LORAs2Points lor(c1, c2); -// bin = proj_data_info.get_bin(lor); -// } -// } - -// void -// CListEventDataPETSIRD::get_detection_position_pair(DetectionPositionPair<>& det_pos_pair) -// { -// det_pos_pair.pos1().radial_coord() = layerA; -// det_pos_pair.pos2().radial_coord() = layerB; - -// det_pos_pair.pos1().axial_coord() = ringA; -// det_pos_pair.pos2().axial_coord() = ringB; - -// det_pos_pair.pos1().tangential_coord() = detA; -// det_pos_pair.pos2().tangential_coord() = detB; -// } - -} // namespace detail END_NAMESPACE_STIR diff --git a/src/listmode_buildblock/CListModeDataPETSIRD.cxx b/src/listmode_buildblock/CListModeDataPETSIRD.cxx index ce183d3301..7b8ba41dd8 100644 --- a/src/listmode_buildblock/CListModeDataPETSIRD.cxx +++ b/src/listmode_buildblock/CListModeDataPETSIRD.cxx @@ -38,7 +38,8 @@ Coincidence LM Data Class for PETSIRD: Implementation #include "stir/Succeeded.h" #include "stir/info.h" #include "stir/error.h" -#include "binary/protocols.h" + +// #include "binary/protocols.h" // #include "helpers/include/petsird_helpers.h" // #include "helpers/include/petsird_helpers/create.h" // #include "helpers/include/petsird_helpers/geometry.h" @@ -47,11 +48,6 @@ Coincidence LM Data Class for PETSIRD: Implementation #include "stir/listmode/CListModeDataPETSIRD.h" // #include "stir/listmode/CListRecordPETSIRD.h" -using std::ios; -using std::fstream; -using std::ifstream; -using std::istream; - START_NAMESPACE_STIR CListModeDataPETSIRD::CListModeDataPETSIRD(const std::string& listmode_filename, diff --git a/src/listmode_buildblock/CMakeLists.txt b/src/listmode_buildblock/CMakeLists.txt index 433476fd0d..78fce1cb7e 100644 --- a/src/listmode_buildblock/CMakeLists.txt +++ b/src/listmode_buildblock/CMakeLists.txt @@ -75,19 +75,25 @@ if (HAVE_PETSIRD) #set(PETSIRD_dir ../../PETSIRD/cpp/generated) #add_subdirectory(${PETSIRD_dir} PETSIRD_generated) -target_include_directories(listmode_buildblock PUBLIC - $ - $ -) - -target_include_directories(listmode_buildblock PUBLIC - $ - $ -) +# target_include_directories(listmode_buildblock PUBLIC +# $ +# $ +# ) + +# target_include_directories(listmode_buildblock PUBLIC +# $ +# $ +# ) + +# target_include_directories(listmode_buildblock PUBLIC +# $ +# $ +# ) + +# target_include_directories(listmode_buildblock PUBLIC +# $ +# $ +# ) -target_include_directories(listmode_buildblock PUBLIC - $ - $ -) #target_link_libraries(IO PUBLIC petsird_generated) endif() From ddc06a567558a0571fc3eab588febe6b77046e0d Mon Sep 17 00:00:00 2001 From: NikEfth Date: Wed, 16 Jul 2025 18:27:39 -0400 Subject: [PATCH 19/42] Compiles and LInks --- CMakeLists.txt | 2 - src/IO/CMakeLists.txt | 52 ++++++++++++------- src/IO/PETSIRDCListmodeInputFileFormat.cxx | 23 ++++---- .../stir/IO/PETSIRDCListmodeInputFileFormat.h | 10 ++-- .../stir/listmode/CListModeDataPETSIRD.h | 17 +++--- .../CListModeDataPETSIRD.cxx | 24 ++++----- 6 files changed, 65 insertions(+), 63 deletions(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index d590e607af..cf0e6b1131 100755 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -297,8 +297,6 @@ if(NOT DISABLE_PETSIRD) RESULT_VARIABLE JUST_RESULT ) - # set(PETSIRD_dir ../../PETSIRD/cpp/generated) - add_subdirectory(${PROJECT_SOURCE_DIR}/PETSIRD/cpp petsird_generated) if(JUST_RESULT EQUAL 0) set(HAVE_PETSIRD TRUE) set(PETSIRD_base_dir ${PROJECT_SOURCE_DIR}/PETSIRD/cpp/generated) diff --git a/src/IO/CMakeLists.txt b/src/IO/CMakeLists.txt index e05c516018..a9fecadcd3 100644 --- a/src/IO/CMakeLists.txt +++ b/src/IO/CMakeLists.txt @@ -81,7 +81,7 @@ endif() if (HAVE_PETSIRD) list(APPEND ${dir_LIB_SOURCES} -PETSIRDCListmodeInputFileFormat.cxx + PETSIRDCListmodeInputFileFormat.cxx ) endif() @@ -145,34 +145,46 @@ if (NOT MINI_STIR) endif() if (HAVE_PETSIRD) - #set(PETSIRD_dir ../../PETSIRD/cpp/generated) +# #set(PETSIRD_dir ../../PETSIRD/cpp/generated) -target_include_directories(IO PUBLIC - $ - $ - $ - $ -) +# target_include_directories(IO PUBLIC +# $ +# $ +# $ +# $ +# ) -# # target_include_directories(IO PUBLIC -# # $ -# # $ -# # ) +# # # target_include_directories(IO PUBLIC +# # # $ +# # # $ +# # # ) +# # # target_include_directories(IO PUBLIC +# # # $ +# # # $ +# # # ) # # target_include_directories(IO PUBLIC -# # $ +# # $ +# # $ +# # $ # # $ # # ) -# target_include_directories(IO PUBLIC -# $ -# $ -# $ -# $ -# ) -# target_link_libraries(IO PUBLIC petsird_generated) +# # target_link_libraries(IO PUBLIC petsird_generated) + +set(PETSIRD_dir ../../PETSIRD/cpp/generated) +add_subdirectory(${PETSIRD_dir} PETSIRD_generated) +# install(TARGETS petsird_generated +# EXPORT STIRTargets +# DESTINATION lib) + +# target_include_directories(IO PUBLIC ${PETSIRD_dir}) +# # needed for helpers +# target_include_directories(IO PUBLIC ${PETSIRD_dir}/..) +# target_include_directories(IO PUBLIC ${PETSIRD_dir}/../helpers/include) +target_link_libraries(IO PUBLIC petsird_generated) endif() diff --git a/src/IO/PETSIRDCListmodeInputFileFormat.cxx b/src/IO/PETSIRDCListmodeInputFileFormat.cxx index 52fe2a869d..982295c673 100644 --- a/src/IO/PETSIRDCListmodeInputFileFormat.cxx +++ b/src/IO/PETSIRDCListmodeInputFileFormat.cxx @@ -1,5 +1,6 @@ #include "stir/IO/PETSIRDCListmodeInputFileFormat.h" #include "../../PETSIRD/cpp/generated/binary/protocols.h" +#include "../../PETSIRD/cpp/generated/hdf5/protocols.h" // #include "../../PETSIRD/cpp/generated/types.h" // #include "../../PETSIRD/cpp/helpers/include/petsird_helpers.h" @@ -9,18 +10,20 @@ bool PETSIRDCListmodeInputFileFormat::can_read(const FileSignature& signature, const std::string& filename) const { - int nikos = 0; - nikos += 40; + petsird::hdf5::PETSIRDReader* petsird_reader = new petsird::hdf5::PETSIRDReader(filename); - if (nikos == 40) - std::cout << filename << std::endl; + if(is_null_ptr(petsird_reader)) + { - petsird::Header header; - // petsird::binary::PETSIRDReader petsird_reader(filename); - // petsird_reader.ReadHeader(header); - // petsird::ScannerInformation scanner_info = header.scanner; - // petsird::ScannerGeometry scanner_geo = scanner_info.scanner_geometry; - return true; // cannot read from istream + petsird::binary::PETSIRDReader* petsird_reader = new petsird::binary::PETSIRDReader(filename); + if(is_null_ptr(petsird_reader)) + { + return false; + } + return true; + } + + return true; } END_NAMESPACE_STIR diff --git a/src/include/stir/IO/PETSIRDCListmodeInputFileFormat.h b/src/include/stir/IO/PETSIRDCListmodeInputFileFormat.h index 1caffe327b..13f04ab952 100644 --- a/src/include/stir/IO/PETSIRDCListmodeInputFileFormat.h +++ b/src/include/stir/IO/PETSIRDCListmodeInputFileFormat.h @@ -59,11 +59,7 @@ class PETSIRDCListmodeInputFileFormat : public InputFileFormat bool can_read(const FileSignature& signature, const std::string& filename) const override; protected: - bool actual_can_read(const FileSignature& signature, std::istream& input) const override - { - int nikos = 0; - return true; - } + bool actual_can_read(const FileSignature& signature, std::istream& input) const override { return false; } public: unique_ptr read_from_file(std::istream& input) const override @@ -74,8 +70,8 @@ class PETSIRDCListmodeInputFileFormat : public InputFileFormat unique_ptr read_from_file(const std::string& filename) const override { - int nikos = 0; - // return unique_ptr(new CListModeDataPETSIRD(filename)); + info("PETSIRDCListmodeInputFileFormat: read_from_file(" + std::string(filename) + ")"); + return unique_ptr(new CListModeDataPETSIRD(filename)); } }; END_NAMESPACE_STIR diff --git a/src/include/stir/listmode/CListModeDataPETSIRD.h b/src/include/stir/listmode/CListModeDataPETSIRD.h index 3701793a58..ca375eb17a 100644 --- a/src/include/stir/listmode/CListModeDataPETSIRD.h +++ b/src/include/stir/listmode/CListModeDataPETSIRD.h @@ -60,25 +60,20 @@ START_NAMESPACE_STIR class CListModeDataPETSIRD : public CListModeDataBasedOnCoordinateMap { public: - CListModeDataPETSIRD(const std::string& listmode_filename) - { - CListModeDataBasedOnCoordinateMap::listmode_filename = listmode_filename; - } - CListModeDataPETSIRD(const std::string& listmode_filename, - const std::string& crystal_map_filename, - const std::string& template_proj_data_filename, - const double lor_randomization_sigma = 0.0); + CListModeDataPETSIRD(const std::string& listmode_filename); - shared_ptr get_empty_record_sptr() const override { return nullptr; } + shared_ptr get_empty_record_sptr() const override; - Succeeded get_next_record(CListRecord& record_of_general_type) const override { return Succeeded::no; } + Succeeded get_next_record(CListRecord& record_of_general_type) const override; - virtual shared_ptr> get_current_lm_file() override {} + virtual shared_ptr> get_current_lm_file() override { return current_lm_data_ptr; } bool has_delayeds() const override { return false; } protected: virtual Succeeded open_lm_file() const override; + + mutable shared_ptr> current_lm_data_ptr; }; END_NAMESPACE_STIR diff --git a/src/listmode_buildblock/CListModeDataPETSIRD.cxx b/src/listmode_buildblock/CListModeDataPETSIRD.cxx index 7b8ba41dd8..ed69a7aa2c 100644 --- a/src/listmode_buildblock/CListModeDataPETSIRD.cxx +++ b/src/listmode_buildblock/CListModeDataPETSIRD.cxx @@ -39,7 +39,9 @@ Coincidence LM Data Class for PETSIRD: Implementation #include "stir/info.h" #include "stir/error.h" -// #include "binary/protocols.h" +#include "../../PETSIRD/cpp/generated/binary/protocols.h" +#include "../../PETSIRD/cpp/generated/hdf5/protocols.h" + // #include "helpers/include/petsird_helpers.h" // #include "helpers/include/petsird_helpers/create.h" // #include "helpers/include/petsird_helpers/geometry.h" @@ -50,23 +52,21 @@ Coincidence LM Data Class for PETSIRD: Implementation START_NAMESPACE_STIR -CListModeDataPETSIRD::CListModeDataPETSIRD(const std::string& listmode_filename, - const std::string& crystal_map_filename, - const std::string& template_proj_data_filename, - const double lor_randomization_sigma) +CListModeDataPETSIRD::CListModeDataPETSIRD(const std::string& listmode_filename) { CListModeDataBasedOnCoordinateMap::listmode_filename = listmode_filename; - petsird::Header header; - petsird::binary::PETSIRDReader petsird_reader(listmode_filename); - petsird_reader.ReadHeader(header); - petsird::ScannerInformation scanner_info = header.scanner; - petsird::ScannerGeometry scanner_geo = scanner_info.scanner_geometry; + // petsird::Header header; + // petsird::binary::PETSIRDReader petsird_reader(listmode_filename); + // petsird_reader.ReadHeader(header); + // petsird::ScannerInformation scanner_info = header.scanner; + // petsird::ScannerGeometry scanner_geo = scanner_info.scanner_geometry; + // need to get inner_ring_radius, num_detector_layers, num_transaxial_crystals_per_block, num_axial_crystals_per_block // transaxial_crystal_spacing,average_depth_of_interaction,axial_crystal_spacing, num_rings, ring_spacing, num_axial_blocks, // num_oftransaxial_blocks // these are from rep_module.object - std::vector replicated_module_list = scanner_geo.replicated_modules; + // std::vector replicated_module_list = scanner_geo.replicated_modules; // petsird::ReplicatedDetectorModule = scanner_geo.replicated_modules; // if (!crystal_map_filename.empty()) @@ -110,6 +110,4 @@ CListModeDataPETSIRD::open_lm_file() const // return Succeeded::yes; } -// template class CListModeDataPETSIRD; - END_NAMESPACE_STIR From 3a4ee34592ee4ff08e330a542ddc3e5a22154b27 Mon Sep 17 00:00:00 2001 From: NikEfth Date: Wed, 16 Jul 2025 20:45:49 -0400 Subject: [PATCH 20/42] Finished all the infrastrucure. We can now focus on reading events, --- CMakeLists.txt | 5 + src/IO/CMakeLists.txt | 5 +- .../CListModeDataBasedOnCoordinateMap.h | 54 +------ .../stir/listmode/CListModeDataPETSIRD.h | 17 ++- .../stir/listmode/CListModeDataSAFIR.h | 8 +- .../stir/listmode/CListRecordPETSIRD.h | 136 ++++-------------- .../stir/listmode/CListRecordPETSIRD.inl | 16 ++- .../CListModeDataBasedOnCoordinateMap.cxx | 7 - .../CListModeDataPETSIRD.cxx | 45 +++--- .../CListModeDataSAFIR.cxx | 7 + src/listmode_buildblock/CMakeLists.txt | 2 +- 11 files changed, 95 insertions(+), 207 deletions(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index cf0e6b1131..3021aeb9b8 100755 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -305,6 +305,11 @@ if(NOT DISABLE_PETSIRD) set(HAVE_PETSIRD FALSE) message(WARNING "PETSIRD generation failed with code ${JUST_RESULT}. HAVE_PETSIRD not set.") endif() + set(PETSIRD_dir ${PROJECT_SOURCE_DIR}/PETSIRD/cpp/generated) + add_subdirectory(${PETSIRD_dir} PETSIRD_generated) + install(TARGETS petsird_generated + EXPORT STIRTargets + DESTINATION lib) endif() #### enable support for ctest diff --git a/src/IO/CMakeLists.txt b/src/IO/CMakeLists.txt index a9fecadcd3..3fac0a1add 100644 --- a/src/IO/CMakeLists.txt +++ b/src/IO/CMakeLists.txt @@ -174,8 +174,9 @@ if (HAVE_PETSIRD) # # target_link_libraries(IO PUBLIC petsird_generated) -set(PETSIRD_dir ../../PETSIRD/cpp/generated) -add_subdirectory(${PETSIRD_dir} PETSIRD_generated) +# set(PETSIRD_dir ../../PETSIRD/cpp/generated) +# add_subdirectory(${PETSIRD_dir} PETSIRD_generated) + # install(TARGETS petsird_generated # EXPORT STIRTargets # DESTINATION lib) diff --git a/src/include/stir/listmode/CListModeDataBasedOnCoordinateMap.h b/src/include/stir/listmode/CListModeDataBasedOnCoordinateMap.h index ee32535398..babffd05db 100644 --- a/src/include/stir/listmode/CListModeDataBasedOnCoordinateMap.h +++ b/src/include/stir/listmode/CListModeDataBasedOnCoordinateMap.h @@ -51,14 +51,12 @@ class CListModeDataBasedOnCoordinateMap : public CListModeData { public: std::string get_name() const override; - // shared_ptr get_empty_record_sptr() const override; - // Succeeded get_next_record(CListRecord& record_of_general_type) const override; - Succeeded reset() override; - virtual shared_ptr> get_current_lm_file() = 0; + // virtual shared_ptr> get_current_lm_file() = 0; - SavedPosition save_get_position() override { return static_cast(get_current_lm_file()->save_get_position()); } - Succeeded set_get_position(const SavedPosition& pos) override { return get_current_lm_file()->set_get_position(pos); } + SavedPosition save_get_position() override = 0; + + Succeeded set_get_position(const SavedPosition& pos) override = 0; protected: std::string listmode_filename; @@ -69,50 +67,6 @@ class CListModeDataBasedOnCoordinateMap : public CListModeData shared_ptr map; }; -// CListModeDataBasedOnCoordinateMap::CListModeDataBasedOnCoordinateMap(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) -// { -// if (!crystal_map_filename.empty()) -// { -// map = MAKE_SHARED(crystal_map_filename, lor_randomization_sigma); -// } -// else -// { -// if (lor_randomization_sigma != 0) -// error("SAFIR currently does not support LOR-randomisation unless a map is specified"); -// } -// shared_ptr _exam_info_sptr(new ExamInfo); -// _exam_info_sptr->imaging_modality = ImagingModality::PT; -// this->exam_info_sptr = _exam_info_sptr; - -// // 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"); -// } -// } - -// // CListModeDataBasedOnCoordinateMap::CListModeDataBasedOnCoordinateMap(const std::string& listmode_filename, -// // const shared_ptr& proj_data_info_sptr) -// // : listmode_filename(listmode_filename) -// // { -// // shared_ptr _exam_info_sptr(new ExamInfo); -// // _exam_info_sptr->imaging_modality = ImagingModality::PT; -// // this->exam_info_sptr = _exam_info_sptr; -// // this->set_proj_data_info_sptr(proj_data_info_sptr->create_shared_clone()); - -// // if (open_lm_file() == Succeeded::no) -// // { -// // error("CListModeDataSAFIR: opening file \"" + listmode_filename + "\""); -// // } -// // } - END_NAMESPACE_STIR #endif diff --git a/src/include/stir/listmode/CListModeDataPETSIRD.h b/src/include/stir/listmode/CListModeDataPETSIRD.h index ca375eb17a..0fe44b39be 100644 --- a/src/include/stir/listmode/CListModeDataPETSIRD.h +++ b/src/include/stir/listmode/CListModeDataPETSIRD.h @@ -47,6 +47,9 @@ Jannis Fischer #include "stir/listmode/CListRecordPETSIRD.h" +#include "../../PETSIRD/cpp/generated/binary/protocols.h" +#include "../../PETSIRD/cpp/generated/hdf5/protocols.h" + START_NAMESPACE_STIR /*! @@ -62,18 +65,22 @@ class CListModeDataPETSIRD : public CListModeDataBasedOnCoordinateMap public: CListModeDataPETSIRD(const std::string& listmode_filename); - shared_ptr get_empty_record_sptr() const override; + virtual shared_ptr get_empty_record_sptr() const override; + + virtual Succeeded get_next_record(CListRecord& record_of_general_type) const override; + + SavedPosition save_get_position() override {} - Succeeded get_next_record(CListRecord& record_of_general_type) const override; + Succeeded set_get_position(const SavedPosition& pos) override {} - virtual shared_ptr> get_current_lm_file() override { return current_lm_data_ptr; } + virtual bool has_delayeds() const override { return true; } - bool has_delayeds() const override { return false; } + Succeeded reset() override {} protected: virtual Succeeded open_lm_file() const override; - mutable shared_ptr> current_lm_data_ptr; + mutable shared_ptr current_lm_data_ptr; }; END_NAMESPACE_STIR diff --git a/src/include/stir/listmode/CListModeDataSAFIR.h b/src/include/stir/listmode/CListModeDataSAFIR.h index c26d2798b0..d8d400daf7 100644 --- a/src/include/stir/listmode/CListModeDataSAFIR.h +++ b/src/include/stir/listmode/CListModeDataSAFIR.h @@ -64,10 +64,14 @@ class CListModeDataSAFIR : public CListModeDataBasedOnCoordinateMap shared_ptr get_empty_record_sptr() const override; Succeeded get_next_record(CListRecordT& record_of_general_type) const override; - virtual shared_ptr> get_current_lm_file() { return current_lm_data_ptr; }; - bool has_delayeds() const override { return false; } + Succeeded reset() override; + + SavedPosition save_get_position() override { return static_cast(current_lm_data_ptr->save_get_position()); } + + Succeeded set_get_position(const SavedPosition& pos) override { return current_lm_data_ptr->set_get_position(pos); } + protected: virtual Succeeded open_lm_file() const override; mutable shared_ptr> current_lm_data_ptr; diff --git a/src/include/stir/listmode/CListRecordPETSIRD.h b/src/include/stir/listmode/CListRecordPETSIRD.h index e05d020851..d71043e12a 100644 --- a/src/include/stir/listmode/CListRecordPETSIRD.h +++ b/src/include/stir/listmode/CListRecordPETSIRD.h @@ -36,12 +36,9 @@ Coincidence Event Class for PETSIRD: Header File #ifndef __stir_listmode_CListRecordPETSIRD_H__ #define __stir_listmode_CListRecordPETSIRD_H__ -#include - #include "stir/listmode/CListRecord.h" #include "stir/DetectionPositionPair.h" #include "stir/Succeeded.h" -#include "stir/ByteOrder.h" #include "stir/ByteOrderDefine.h" #include "boost/static_assert.hpp" @@ -62,9 +59,6 @@ coordinates to specify LORAs2Points from given detection pair indices. class CListEventPETSIRD : public CListEvent { public: - /*! Default constructor will not work as it does not initialize a map to relate - detector indices and space coordinates. Always use either set_scanner_sptr or set_map_sptr after default construction. - */ inline CListEventPETSIRD() {} //! Returns LOR corresponding to the given event. @@ -73,22 +67,16 @@ class CListEventPETSIRD : public CListEvent //! Override the default implementation inline void get_bin(Bin& bin, const ProjDataInfo& proj_data_info) const override; - //! This method checks if the template is valid for LmToProjData - /*! Used before the actual processing of the data (see issue #61), before calling get_bin() - * Most scanners have listmode data that correspond to non arc-corrected data and - * this check avoids a crash when an unsupported template is used as input. - */ - inline bool is_valid_template(const ProjDataInfo&) const override { return true; } - //! Returns 0 if event is prompt and 1 if delayed - inline bool is_prompt() const override { return true; } //!(static_cast(this)->is_prompt()); } - //! Function to set map for detector indices to coordinates. - /*! Use a null pointer to disable the mapping functionality */ + inline bool is_prompt() const override { return true; } + inline void set_map_sptr(shared_ptr new_map_sptr) { map_sptr = new_map_sptr; } /*! Set the scanner */ /*! Currently only used if the map is not set. */ inline void set_scanner_sptr(shared_ptr new_scanner_sptr) { scanner_sptr = new_scanner_sptr; } + virtual bool is_valid_template(const ProjDataInfo&) const override { return true; } + private: shared_ptr map_sptr; shared_ptr scanner_sptr; @@ -96,54 +84,9 @@ class CListEventPETSIRD : public CListEvent const DetectorCoordinateMap& map_to_use() const { return map_sptr ? *map_sptr : *this->scanner_sptr->get_detector_map_sptr(); } }; -// //! Class for record with coincidence data using PETSIRD bitfield definition -// /*! \ingroup listmode */ -// class CListEventDataPETSIRD -// { -// public: -// //! Writes detection position pair to reference given as argument. -// inline void get_detection_position_pair(DetectionPositionPair<>& det_pos_pair); - -// //! Returns 0 if event is prompt and 1 if delayed -// inline bool is_prompt() const { return !isDelayed; } - -// //! Returns 1 if if event is time and 0 if it is prompt -// inline bool is_time() const { return type; } - -// //! Can be used to set "promptness" of event. -// inline Succeeded set_prompt(const bool prompt = true) -// { -// isDelayed = !prompt; -// return Succeeded::yes; -// } - -// private: -// #if STIRIsNativeByteOrderBigEndian -// unsigned type : 1; -// unsigned isDelayed : 1; -// unsigned reserved : 6; -// unsigned layerB : 4; -// unsigned layerA : 4; -// unsigned detB : 16; -// unsigned detA : 16; -// unsigned ringB : 8; -// unsigned ringA : 8; -// #else -// unsigned ringA : 8; -// unsigned ringB : 8; -// unsigned detA : 16; -// unsigned detB : 16; -// unsigned layerA : 4; -// unsigned layerB : 4; -// unsigned reserved : 6; -// unsigned isDelayed : 1; -// unsigned type : 1; -// #endif -// }; - //! Class for record with time data using PETSIRD bitfield definition /*! \ingroup listmode */ -class CListTimeDataPETSIRD +class CListTimePETSIRD : public ListTime { public: inline unsigned long get_time_in_millisecs() const { /*return static_cast(time);*/ } @@ -155,73 +98,42 @@ class CListTimeDataPETSIRD inline bool is_time() const { /*return type; */ } }; -class CListRecordPETSIRD : public CListRecord, public ListTime, public CListEventPETSIRD +class CListRecordPETSIRD : public CListRecord { public: - //! Returns event_data (without checking if the type is really event and not time). - CListEventPETSIRD get_data() const { return this->event_data; } - - // CListRecordPETSIRD() - // : CListEventPETSIRD>() - // {} + CListRecordPETSIRD() {} ~CListRecordPETSIRD() override {} - bool is_time() const override { return time_data.is_time(); } - - bool is_event() const override { return !time_data.is_time(); } - - CListEvent& event() override { return *this; } - - const CListEvent& event() const override { return *this; } - - virtual CListEventPETSIRD& event_PETSIRD() { return *this; } + bool is_time() const override { /*return time_data.is_time();*/ } - virtual const CListEventPETSIRD& event_PETSIRD() const { return *this; } + bool is_event() const override { /*return !time_data.is_time();*/ } - ListTime& time() override { return *this; } + ListEvent& event() override { return event_data; } + const ListEvent& event() const override { return event_data; } - const ListTime& time() const override { return *this; } + ListTime& time() override { return time_data; } + const ListTime& time() const override { return time_data; } - virtual bool operator==(const CListRecord& e2) const - { - return dynamic_cast(&e2) != 0 && raw == static_cast(e2).raw; - } - - inline unsigned long get_time_in_millisecs() const override { return time_data.get_time_in_millisecs(); } - - inline Succeeded set_time_in_millisecs(const unsigned long time_in_millisecs) override - { - return time_data.set_time_in_millisecs(time_in_millisecs); - } + // virtual bool operator==(const CListRecordPETSIRD& e2) const + // { + // // return dynamic_cast(&e2) != 0 && raw == static_cast(e2).r; + // } - inline bool is_prompt() const override { return event_data.is_prompt(); } + // inline bool is_prompt() const override { /*return event_data.is_prompt();*/ } Succeeded init_from_data_ptr(const char* const data_ptr, const std::size_t size_of_record, const bool do_byte_swap) { - assert(size_of_record >= 8); - std::copy(data_ptr, data_ptr + 8, reinterpret_cast(&raw)); // TODO necessary for operator== - if (do_byte_swap) - ByteOrder::swap_order(raw); + // assert(size_of_record >= 8); + // std::copy(data_ptr, data_ptr + 8, reinterpret_cast(&raw)); // TODO necessary for operator== + // if (do_byte_swap) + // ByteOrder::swap_order(raw); return Succeeded::yes; } - std::size_t size_of_record_at_ptr(const char* const /*data_ptr*/, const std::size_t /*size*/, const bool /*do_byte_swap*/) const - { - return 8; - } - private: - // use C++ union to save data, you can only use one at a time, - // but compiler will not check which one was used! - // Be careful not to read event data from time record and vice versa!! - // However, this is used as a feature if comparing events over the 'raw' type. - union - { - CListEventPETSIRD event_data; - CListTimeDataPETSIRD time_data; - boost::int64_t raw; - }; + CListEventPETSIRD event_data; + CListTimePETSIRD time_data; }; END_NAMESPACE_STIR diff --git a/src/include/stir/listmode/CListRecordPETSIRD.inl b/src/include/stir/listmode/CListRecordPETSIRD.inl index 6f4dbdbc2f..933cf7a944 100644 --- a/src/include/stir/listmode/CListRecordPETSIRD.inl +++ b/src/include/stir/listmode/CListRecordPETSIRD.inl @@ -54,15 +54,19 @@ START_NAMESPACE_STIR LORAs2Points CListEventPETSIRD::get_LOR() const { - LORAs2Points lor; - DetectionPositionPair<> det_pos_pair; + // LORAs2Points lor; + // DetectionPositionPair<> det_pos_pair; - // static_cast(this)->get_data().get_detection_position_pair(det_pos_pair); + // // static_cast(this)->get_data().get_detection_position_pair(det_pos_pair); - lor.p1() = map_to_use().get_coordinate_for_index(det_pos_pair.pos1()); - lor.p2() = map_to_use().get_coordinate_for_index(det_pos_pair.pos2()); + // lor.p1() = map_to_use().get_coordinate_for_index(det_pos_pair.pos1()); + // lor.p2() = map_to_use().get_coordinate_for_index(det_pos_pair.pos2()); - return lor; + // return lor; } +void +CListEventPETSIRD::get_bin(Bin& bin, const ProjDataInfo& proj_data_info) const +{} + END_NAMESPACE_STIR diff --git a/src/listmode_buildblock/CListModeDataBasedOnCoordinateMap.cxx b/src/listmode_buildblock/CListModeDataBasedOnCoordinateMap.cxx index 8f3564563a..fda23351d5 100644 --- a/src/listmode_buildblock/CListModeDataBasedOnCoordinateMap.cxx +++ b/src/listmode_buildblock/CListModeDataBasedOnCoordinateMap.cxx @@ -47,11 +47,4 @@ CListModeDataBasedOnCoordinateMap::get_name() const return listmode_filename; } - -Succeeded -CListModeDataBasedOnCoordinateMap::reset() -{ - return get_current_lm_file()->reset(); -} - END_NAMESPACE_STIR diff --git a/src/listmode_buildblock/CListModeDataPETSIRD.cxx b/src/listmode_buildblock/CListModeDataPETSIRD.cxx index ed69a7aa2c..febebf78ec 100644 --- a/src/listmode_buildblock/CListModeDataPETSIRD.cxx +++ b/src/listmode_buildblock/CListModeDataPETSIRD.cxx @@ -39,22 +39,20 @@ Coincidence LM Data Class for PETSIRD: Implementation #include "stir/info.h" #include "stir/error.h" -#include "../../PETSIRD/cpp/generated/binary/protocols.h" -#include "../../PETSIRD/cpp/generated/hdf5/protocols.h" - // #include "helpers/include/petsird_helpers.h" // #include "helpers/include/petsird_helpers/create.h" // #include "helpers/include/petsird_helpers/geometry.h" // #include "boost/static_assert.hpp" #include "stir/listmode/CListModeDataPETSIRD.h" -// #include "stir/listmode/CListRecordPETSIRD.h" +#include "stir/listmode/CListRecordPETSIRD.h" START_NAMESPACE_STIR CListModeDataPETSIRD::CListModeDataPETSIRD(const std::string& listmode_filename) { - CListModeDataBasedOnCoordinateMap::listmode_filename = listmode_filename; + this->listmode_filename = listmode_filename; + // petsird::Header header; // petsird::binary::PETSIRDReader petsird_reader(listmode_filename); // petsird_reader.ReadHeader(header); @@ -86,28 +84,31 @@ CListModeDataPETSIRD::CListModeDataPETSIRD(const std::string& listmode_filename) // 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 (this->open_lm_file() == Succeeded::no) - // { - // error("CListModeDataPETSIRD: Could not open listmode file " + listmode_filename + "\n"); - // } + if (this->open_lm_file() == Succeeded::no) + { + error("CListModeDataPETSIRD: Could not open listmode file " + listmode_filename + "\n"); + } } Succeeded CListModeDataPETSIRD::open_lm_file() const { - // shared_ptr stream_ptr(new fstream(this->listmode_filename.c_str(), ios::in | ios::binary)); - // if (!(*stream_ptr)) - // { - // return Succeeded::no; - // } - // info("CListModeDataPETSIRD: opening file \"" + this->listmode_filename + "\"", 2); - // stream_ptr->seekg((std::streamoff)32); - // this->current_lm_data_ptr.reset( - // new InputStreamWithRecords(stream_ptr, - // sizeof(CListTimeDataPETSIRD), - // sizeof(CListTimeDataPETSIRD), - // ByteOrder::little_endian != ByteOrder::get_native_order())); - // return Succeeded::yes; + current_lm_data_ptr.reset(new petsird::hdf5::PETSIRDReader(listmode_filename)); + return Succeeded::yes; +} + +shared_ptr +CListModeDataPETSIRD::get_empty_record_sptr() const +{ + shared_ptr sptr(new CListRecordPETSIRD()); + return sptr; +} + +Succeeded +CListModeDataPETSIRD::get_next_record(CListRecord& record_of_general_type) const +{ + CListRecordPETSIRD& record = dynamic_cast(record_of_general_type); + // return current_lm_data_ptr->get_next_record(record); } END_NAMESPACE_STIR diff --git a/src/listmode_buildblock/CListModeDataSAFIR.cxx b/src/listmode_buildblock/CListModeDataSAFIR.cxx index 7d1a0dc5e5..e584b6fdd6 100644 --- a/src/listmode_buildblock/CListModeDataSAFIR.cxx +++ b/src/listmode_buildblock/CListModeDataSAFIR.cxx @@ -133,6 +133,13 @@ CListModeDataSAFIR::get_next_record(CListRecordT& record_of_genera return status; } +template +Succeeded +CListModeDataSAFIR::reset() +{ + return current_lm_data_ptr->reset(); +} + // template class CListModeDataSAFIR>; // template class CListModeDataSAFIR>; diff --git a/src/listmode_buildblock/CMakeLists.txt b/src/listmode_buildblock/CMakeLists.txt index 78fce1cb7e..20729259c6 100644 --- a/src/listmode_buildblock/CMakeLists.txt +++ b/src/listmode_buildblock/CMakeLists.txt @@ -95,5 +95,5 @@ if (HAVE_PETSIRD) # $ # ) -#target_link_libraries(IO PUBLIC petsird_generated) +target_link_libraries(listmode_buildblock PUBLIC petsird_generated) endif() From d7a00cca96852a27c2cd0a713272a3273dd15bd2 Mon Sep 17 00:00:00 2001 From: danieldeidda Date: Thu, 17 Jul 2025 16:43:53 +0100 Subject: [PATCH 21/42] reading header and extracting averag position of each detector --- .gitmodules | 3 ++ PETSIRD | 1 + .../CListModeDataPETSIRD.cxx | 49 +++++++++++++++---- 3 files changed, 43 insertions(+), 10 deletions(-) create mode 160000 PETSIRD diff --git a/.gitmodules b/.gitmodules index 5d1ac04565..ec9680161d 100644 --- a/.gitmodules +++ b/.gitmodules @@ -1,3 +1,6 @@ [submodule "external_helpers/fmt"] path = external_helpers/fmt url = https://github.com/fmtlib/fmt.git +[submodule "PETSIRD"] + path = PETSIRD + url = https://github.com/ETSInitiative/PETSIRD.git diff --git a/PETSIRD b/PETSIRD new file mode 160000 index 0000000000..96c1dc0120 --- /dev/null +++ b/PETSIRD @@ -0,0 +1 @@ +Subproject commit 96c1dc0120fea2900b504fad1ab241aa2b1bd7d9 diff --git a/src/listmode_buildblock/CListModeDataPETSIRD.cxx b/src/listmode_buildblock/CListModeDataPETSIRD.cxx index febebf78ec..3d88e212ff 100644 --- a/src/listmode_buildblock/CListModeDataPETSIRD.cxx +++ b/src/listmode_buildblock/CListModeDataPETSIRD.cxx @@ -39,9 +39,10 @@ Coincidence LM Data Class for PETSIRD: Implementation #include "stir/info.h" #include "stir/error.h" -// #include "helpers/include/petsird_helpers.h" +#include "binary/protocols.h" +#include "helpers/include/petsird_helpers.h" // #include "helpers/include/petsird_helpers/create.h" -// #include "helpers/include/petsird_helpers/geometry.h" +#include "helpers/include/petsird_helpers/geometry.h" // #include "boost/static_assert.hpp" #include "stir/listmode/CListModeDataPETSIRD.h" @@ -53,19 +54,47 @@ CListModeDataPETSIRD::CListModeDataPETSIRD(const std::string& listmode_filename) { this->listmode_filename = listmode_filename; - // petsird::Header header; - // petsird::binary::PETSIRDReader petsird_reader(listmode_filename); - // petsird_reader.ReadHeader(header); - // petsird::ScannerInformation scanner_info = header.scanner; - // petsird::ScannerGeometry scanner_geo = scanner_info.scanner_geometry; - + petsird::Header header; + petsird::binary::PETSIRDReader petsird_reader(listmode_filename); + petsird_reader.ReadHeader(header); + petsird::ScannerInformation scanner_info = header.scanner; + petsird::ScannerGeometry scanner_geo = scanner_info.scanner_geometry; + const petsird::TypeOfModule type_of_module{ 0 }; // need to get inner_ring_radius, num_detector_layers, num_transaxial_crystals_per_block, num_axial_crystals_per_block // transaxial_crystal_spacing,average_depth_of_interaction,axial_crystal_spacing, num_rings, ring_spacing, num_axial_blocks, // num_oftransaxial_blocks // these are from rep_module.object - // std::vector replicated_module_list = scanner_geo.replicated_modules; - // petsird::ReplicatedDetectorModule = scanner_geo.replicated_modules; + std::vector replicated_module_list = scanner_geo.replicated_modules; + int num_modules = scanner_geo.replicated_modules[type_of_module].transforms.size(); + int num_elements_per_module = scanner_geo.replicated_modules[type_of_module].object.detecting_elements.transforms.size(); + const auto& tof_bin_edges = header.scanner.tof_bin_edges[type_of_module][type_of_module]; + const auto num_tof_bins = tof_bin_edges.NumberOfBins(); + const auto& event_energy_bin_edges = header.scanner.event_energy_bin_edges[type_of_module]; + const auto num_event_energy_bins = event_energy_bin_edges.NumberOfBins(); + // coordinates of first detecting bin (module_id,element_id, energy_id) + // const petsird::ExpandedDetectionBin expanded_detection_bin{ 0, 0, 0 }; + const auto box_shape = petsird_helpers::geometry::get_detecting_box(header.scanner, type_of_module, expanded_detection_bin); + // get center of box (this should be in a loop to create a map + + for (uint32_t module = 0; module < num_modules; module++) + for (uint32_t elem = 0; elem < num_elements_per_module; elem++) + for (uint32_t ener = 0; ener < num_event_energy_bins; ener++) + { + petsird::ExpandedDetectionBin expanded_detection_bin{ module, elem, ener }; + const auto box_shape + = petsird_helpers::geometry::get_detecting_box(header.scanner, type_of_module, expanded_detection_bin); + CartesianCoordinate3D mean_pos; + for (auto& corner : box_shape.corners) + { // if STIR (z,y,x) -> PETSIRD (-y, -x, z) pheraps the order below needs to be changed + mean_pos.x() = +corner.c[0] / box_shape.corners.size(); + mean_pos.y() = +corner.c[1] / box_shape.corners.size(); + mean_pos.z() = +corner.c[2] / box_shape.corners.size(); + } + // save mean pos into map + } + + // petsird::ReplicatedDetectorModule = scanner_geo.replicated_modules; // if (!crystal_map_filename.empty()) // { From 32c314499fc7556696803333f7501c5427a9573c Mon Sep 17 00:00:00 2001 From: NikEfth Date: Thu, 17 Jul 2025 19:21:30 -0400 Subject: [PATCH 22/42] Finalizing the Interface with PETSIRD. --- src/IO/CMakeLists.txt | 11 ++- src/IO/PETSIRDCListmodeInputFileFormat.cxx | 43 ++++++++-- src/include/stir/IO/InputFileFormat.h | 2 +- src/include/stir/IO/InputStreamFromPETSIRD.h | 85 +++++++++++++++++++ .../stir/IO/PETSIRDCListmodeInputFileFormat.h | 6 +- .../stir/IO/SAFIRCListmodeInputFileFormat.h | 2 +- .../stir/listmode/CListModeDataPETSIRD.h | 27 ++++-- .../stir/listmode/CListRecordPETSIRD.h | 9 +- .../CListModeDataPETSIRD.cxx | 49 +++++++++-- src/listmode_buildblock/CMakeLists.txt | 9 +- 10 files changed, 203 insertions(+), 40 deletions(-) create mode 100644 src/include/stir/IO/InputStreamFromPETSIRD.h diff --git a/src/IO/CMakeLists.txt b/src/IO/CMakeLists.txt index 3fac0a1add..0b88f5b43f 100644 --- a/src/IO/CMakeLists.txt +++ b/src/IO/CMakeLists.txt @@ -148,12 +148,11 @@ if (HAVE_PETSIRD) # #set(PETSIRD_dir ../../PETSIRD/cpp/generated) -# target_include_directories(IO PUBLIC -# $ -# $ -# $ -# $ -# ) +target_include_directories(IO PUBLIC + $ + $ + $ +) # # # target_include_directories(IO PUBLIC diff --git a/src/IO/PETSIRDCListmodeInputFileFormat.cxx b/src/IO/PETSIRDCListmodeInputFileFormat.cxx index 982295c673..4352cc9e20 100644 --- a/src/IO/PETSIRDCListmodeInputFileFormat.cxx +++ b/src/IO/PETSIRDCListmodeInputFileFormat.cxx @@ -7,23 +7,48 @@ START_NAMESPACE_STIR bool -PETSIRDCListmodeInputFileFormat::can_read(const FileSignature& signature, const std::string& filename) const +PETSIRDCListmodeInputFileFormat::can_read(const FileSignature& signature, const std::string& filename) { - petsird::hdf5::PETSIRDReader* petsird_reader = new petsird::hdf5::PETSIRDReader(filename); + std::array hdf5_signature = { 'H', 'D', 'F', '5' }; + std::array binary_signature = { 'y', 'a', 'r', 'd' }; - if(is_null_ptr(petsird_reader)) + std::ifstream file(filename, std::ios::binary); + if (!file.is_open()) { + std::cerr << "Cannot open file: " << filename << std::endl; + return false; + } + + std::array signature_{}; + file.read(signature_.data(), signature_.size()); + + if (signature_ == hdf5_signature) + { + use_hdf5 = true; + return use_hdf5; + } - petsird::binary::PETSIRDReader* petsird_reader = new petsird::binary::PETSIRDReader(filename); - if(is_null_ptr(petsird_reader)) - { - return false; - } + if (signature_ == binary_signature) + { + use_hdf5 = false; return true; } - return true; + // petsird::hdf5::PETSIRDReader* petsird_reader = new petsird::hdf5::PETSIRDReader(filename); + + // if (is_null_ptr(petsird_reader)) + // { + + // petsird::binary::PETSIRDReader* petsird_reader = new petsird::binary::PETSIRDReader(filename); + // if (is_null_ptr(petsird_reader)) + // { + // return false; + // } + // return true; + // } + + return false; } END_NAMESPACE_STIR diff --git a/src/include/stir/IO/InputFileFormat.h b/src/include/stir/IO/InputFileFormat.h index 03bcb6a74a..3a2b6bf7a9 100644 --- a/src/include/stir/IO/InputFileFormat.h +++ b/src/include/stir/IO/InputFileFormat.h @@ -46,7 +46,7 @@ class InputFileFormat { return this->actual_can_read(signature, input); } - virtual bool can_read(const FileSignature& signature, const std::string& filename) const + virtual bool can_read(const FileSignature& signature, const std::string& filename) { std::ifstream input; open_read_binary(input, filename); diff --git a/src/include/stir/IO/InputStreamFromPETSIRD.h b/src/include/stir/IO/InputStreamFromPETSIRD.h new file mode 100644 index 0000000000..9040e2b349 --- /dev/null +++ b/src/include/stir/IO/InputStreamFromPETSIRD.h @@ -0,0 +1,85 @@ +// /*! +// \file +// \ingroup IO +// \brief Declaration of class stir::InputStreamFromROOTFile + +// \author Nikos Efthimiou +// \author Harry Tsoumpas +// \author Kris Thielemans +// \author Robert Twyman +// */ +// /* +// * Copyright (C) 2015, 2016 University of Leeds +// Copyright (C) 2016, 2021, 2020, 2021 UCL +// Copyright (C) 2018 University of Hull +// This file is part of STIR. + +// SPDX-License-Identifier: Apache-2.0 + +// See STIR/LICENSE.txt for details +// */ + +// #ifndef __stir_IO_InputStreamFromPETSIRD_H__ +// #define __stir_IO_InputStreamFromPETSIRD_H__ + +// #include "stir/shared_ptr.h" +// #include "stir/Succeeded.h" +// #include "stir/listmode/CListRecordPETSIRD.h" +// #include "stir/RegisteredObject.h" +// #include "stir/error.h" + +// #include "../../PETSIRD/cpp/generated/binary/protocols.h" +// #include "stir/IO/InputStreamWithRecords.h" +// #include "../../PETSIRD/cpp/generated/hdf5/protocols.h" + +// START_NAMESPACE_STIR + +// class InputStreamWithRecordsFromPETSIRD : public InputStreamWithRecords +// { +// public: +// typedef std::vector::size_type SavedPosition; + +// //! Default constructor +// InputStreamFromPETSIRD(std::string filename); + +// ~InputStreamFromPETSIRD() override +// {} +// //! \details Returns the next record in the ROOT file. +// //! The code is adapted from Sadek A. Nehmeh and CR Schmidtlein, +// //! downloaded from here +// virtual Succeeded get_next_record(CListReco*/rdPETSIRD& record) = 0; +// //! Go to the first event. +// inline Succeeded reset(); +// //! Must be called before calling for the first event. +// virtual Succeeded set_up(const std::string& header_path); +// //! Save current position in a vector +// inline SavedPosition save_get_position(); +// //! Set current position +// inline Succeeded set_get_position(const SavedPosition&); +// //! Get the vector with the saved positions +// inline std::vector get_saved_get_positions() const; +// //! Set a vector with saved positions +// inline void set_saved_get_positions(const std::vector&); +// //! Returns the total number of events +// inline unsigned long int get_total_number_of_events() const; + +// inline std::string get_PETSIRD_filename() const; + +// protected: + +// //! The starting position. +// unsigned long int starting_stream_position; +// //! The total number of entries +// unsigned long int nentries; +// //! Current get position +// unsigned long int current_position; +// //! A vector with saved position indices. +// std::vector saved_get_positions; +// //! The name of the ROOT chain to be read +// }; + +// END_NAMESPACE_STIR + +// #include "stir/IO/InputStreamFromROOTFile.inl" + +// #endif diff --git a/src/include/stir/IO/PETSIRDCListmodeInputFileFormat.h b/src/include/stir/IO/PETSIRDCListmodeInputFileFormat.h index 13f04ab952..9c9f2bb07e 100644 --- a/src/include/stir/IO/PETSIRDCListmodeInputFileFormat.h +++ b/src/include/stir/IO/PETSIRDCListmodeInputFileFormat.h @@ -56,11 +56,13 @@ class PETSIRDCListmodeInputFileFormat : public InputFileFormat const std::string get_name() const override { return "PETSIRD"; } //! Checks in binary data file for correct signature. - bool can_read(const FileSignature& signature, const std::string& filename) const override; + bool can_read(const FileSignature& signature, const std::string& filename) override; protected: bool actual_can_read(const FileSignature& signature, std::istream& input) const override { return false; } + bool use_hdf5 = false; + public: unique_ptr read_from_file(std::istream& input) const override { @@ -71,7 +73,7 @@ class PETSIRDCListmodeInputFileFormat : public InputFileFormat unique_ptr read_from_file(const std::string& filename) const override { info("PETSIRDCListmodeInputFileFormat: read_from_file(" + std::string(filename) + ")"); - return unique_ptr(new CListModeDataPETSIRD(filename)); + return unique_ptr(new CListModeDataPETSIRD(filename, use_hdf5)); } }; END_NAMESPACE_STIR diff --git a/src/include/stir/IO/SAFIRCListmodeInputFileFormat.h b/src/include/stir/IO/SAFIRCListmodeInputFileFormat.h index 2ac3fdd96d..ee72a67106 100644 --- a/src/include/stir/IO/SAFIRCListmodeInputFileFormat.h +++ b/src/include/stir/IO/SAFIRCListmodeInputFileFormat.h @@ -91,7 +91,7 @@ class SAFIRCListmodeInputFileFormat : public InputFileFormat, publ //! Checks in binary data file for correct signature (can be either "SAFIR CListModeData", "NeuroLF CListModeData" or "MUPET //! CListModeData"). - bool can_read(const FileSignature& signature, const std::string& filename) const override + bool can_read(const FileSignature& signature, const std::string& filename) override { // Looking for the right key in the parameter file std::ifstream par_file(filename.c_str()); diff --git a/src/include/stir/listmode/CListModeDataPETSIRD.h b/src/include/stir/listmode/CListModeDataPETSIRD.h index 0fe44b39be..3496070017 100644 --- a/src/include/stir/listmode/CListModeDataPETSIRD.h +++ b/src/include/stir/listmode/CListModeDataPETSIRD.h @@ -33,22 +33,16 @@ Jannis Fischer #ifndef __stir_listmode_CListModeDataPETSIRD_H__ #define __stir_listmode_CListModeDataPETSIRD_H__ -#include #include -#include -#include - #include "stir/listmode/CListModeDataBasedOnCoordinateMap.h" #include "stir/ProjData.h" #include "stir/ProjDataInfo.h" #include "stir/listmode/CListRecord.h" -#include "stir/IO/InputStreamWithRecords.h" #include "stir/shared_ptr.h" #include "stir/listmode/CListRecordPETSIRD.h" -#include "../../PETSIRD/cpp/generated/binary/protocols.h" -#include "../../PETSIRD/cpp/generated/hdf5/protocols.h" +#include "../../PETSIRD/cpp/generated/protocols.h" START_NAMESPACE_STIR @@ -63,7 +57,7 @@ START_NAMESPACE_STIR class CListModeDataPETSIRD : public CListModeDataBasedOnCoordinateMap { public: - CListModeDataPETSIRD(const std::string& listmode_filename); + CListModeDataPETSIRD(const std::string& listmode_filename, bool use_hdf5); virtual shared_ptr get_empty_record_sptr() const override; @@ -77,10 +71,25 @@ class CListModeDataPETSIRD : public CListModeDataBasedOnCoordinateMap Succeeded reset() override {} + inline unsigned long int get_total_number_of_events() const override { return total_events; } + protected: virtual Succeeded open_lm_file() const override; - mutable shared_ptr current_lm_data_ptr; + mutable shared_ptr current_lm_data_ptr; + +private: + const bool use_hdf5; + + unsigned long int total_events = 0; + + mutable unsigned long int curr_event_in_event_block = 0; + + mutable petsird::TimeBlock curr_time_block; + + mutable petsird::EventTimeBlock curr_event_block; + + const petsird::TypeOfModulePair type_of_module_pair{ 0, 0 }; }; END_NAMESPACE_STIR diff --git a/src/include/stir/listmode/CListRecordPETSIRD.h b/src/include/stir/listmode/CListRecordPETSIRD.h index d71043e12a..867a98ed78 100644 --- a/src/include/stir/listmode/CListRecordPETSIRD.h +++ b/src/include/stir/listmode/CListRecordPETSIRD.h @@ -41,12 +41,13 @@ Coincidence Event Class for PETSIRD: Header File #include "stir/Succeeded.h" #include "stir/ByteOrderDefine.h" -#include "boost/static_assert.hpp" #include "boost/cstdint.hpp" #include "stir/DetectorCoordinateMap.h" #include "boost/make_shared.hpp" +#include "../../PETSIRD/cpp/generated/types.h" + START_NAMESPACE_STIR /*! @@ -115,6 +116,10 @@ class CListRecordPETSIRD : public CListRecord ListTime& time() override { return time_data; } const ListTime& time() const override { return time_data; } + CListEventPETSIRD& event_PETSIRD() { return event_data; } + + const CListEventPETSIRD& event_PETSIRD() const { return event_data; } + // virtual bool operator==(const CListRecordPETSIRD& e2) const // { // // return dynamic_cast(&e2) != 0 && raw == static_cast(e2).r; @@ -122,7 +127,7 @@ class CListRecordPETSIRD : public CListRecord // inline bool is_prompt() const override { /*return event_data.is_prompt();*/ } - Succeeded init_from_data_ptr(const char* const data_ptr, const std::size_t size_of_record, const bool do_byte_swap) + Succeeded init_from_data_ptr(const petsird::CoincidenceEvent&) { // assert(size_of_record >= 8); // std::copy(data_ptr, data_ptr + 8, reinterpret_cast(&raw)); // TODO necessary for operator== diff --git a/src/listmode_buildblock/CListModeDataPETSIRD.cxx b/src/listmode_buildblock/CListModeDataPETSIRD.cxx index 3d88e212ff..dce71b7148 100644 --- a/src/listmode_buildblock/CListModeDataPETSIRD.cxx +++ b/src/listmode_buildblock/CListModeDataPETSIRD.cxx @@ -44,19 +44,26 @@ Coincidence LM Data Class for PETSIRD: Implementation // #include "helpers/include/petsird_helpers/create.h" #include "helpers/include/petsird_helpers/geometry.h" // #include "boost/static_assert.hpp" +#include "../../PETSIRD/cpp/generated/binary/protocols.h" +#include "../../PETSIRD/cpp/generated/hdf5/protocols.h" #include "stir/listmode/CListModeDataPETSIRD.h" #include "stir/listmode/CListRecordPETSIRD.h" START_NAMESPACE_STIR -CListModeDataPETSIRD::CListModeDataPETSIRD(const std::string& listmode_filename) +CListModeDataPETSIRD::CListModeDataPETSIRD(const std::string& listmode_filename, bool use_hdf5) + : use_hdf5(use_hdf5) { this->listmode_filename = listmode_filename; petsird::Header header; - petsird::binary::PETSIRDReader petsird_reader(listmode_filename); - petsird_reader.ReadHeader(header); + if (use_hdf5) + current_lm_data_ptr.reset(new petsird::hdf5::PETSIRDReader(listmode_filename)); + else + current_lm_data_ptr.reset(new petsird::binary::PETSIRDReader(listmode_filename)); + + current_lm_data_ptr->ReadHeader(header); petsird::ScannerInformation scanner_info = header.scanner; petsird::ScannerGeometry scanner_geo = scanner_info.scanner_geometry; const petsird::TypeOfModule type_of_module{ 0 }; @@ -65,6 +72,15 @@ CListModeDataPETSIRD::CListModeDataPETSIRD(const std::string& listmode_filename) // num_oftransaxial_blocks // these are from rep_module.object + // Get the first TimeBlock + if (current_lm_data_ptr->ReadTimeBlocks(curr_time_block)) + error("CListModeDataPETSIRD: Could not read the first TimeBlock. Abord."); + + if (std::holds_alternative(curr_time_block)) + curr_event_block = std::get(curr_time_block); + else + error("CListModeDataPETSIRD: holds_alternative not true. Abord."); + std::vector replicated_module_list = scanner_geo.replicated_modules; int num_modules = scanner_geo.replicated_modules[type_of_module].transforms.size(); int num_elements_per_module = scanner_geo.replicated_modules[type_of_module].object.detecting_elements.transforms.size(); @@ -73,8 +89,9 @@ CListModeDataPETSIRD::CListModeDataPETSIRD(const std::string& listmode_filename) const auto& event_energy_bin_edges = header.scanner.event_energy_bin_edges[type_of_module]; const auto num_event_energy_bins = event_energy_bin_edges.NumberOfBins(); // coordinates of first detecting bin (module_id,element_id, energy_id) - // const petsird::ExpandedDetectionBin expanded_detection_bin{ 0, 0, 0 }; + const petsird::ExpandedDetectionBin expanded_detection_bin{ 0, 0, 0 }; const auto box_shape = petsird_helpers::geometry::get_detecting_box(header.scanner, type_of_module, expanded_detection_bin); + // get center of box (this should be in a loop to create a map for (uint32_t module = 0; module < num_modules; module++) @@ -84,6 +101,7 @@ CListModeDataPETSIRD::CListModeDataPETSIRD(const std::string& listmode_filename) petsird::ExpandedDetectionBin expanded_detection_bin{ module, elem, ener }; const auto box_shape = petsird_helpers::geometry::get_detecting_box(header.scanner, type_of_module, expanded_detection_bin); + CartesianCoordinate3D mean_pos; for (auto& corner : box_shape.corners) { // if STIR (z,y,x) -> PETSIRD (-y, -x, z) pheraps the order below needs to be changed @@ -129,7 +147,10 @@ CListModeDataPETSIRD::open_lm_file() const shared_ptr CListModeDataPETSIRD::get_empty_record_sptr() const { - shared_ptr sptr(new CListRecordPETSIRD()); + shared_ptr sptr(new CListRecordPETSIRD); + std::dynamic_pointer_cast(sptr)->event_PETSIRD().set_scanner_sptr(this->get_proj_data_info_sptr()->get_scanner_sptr()); + std::dynamic_pointer_cast(sptr)->event_PETSIRD().set_map_sptr(map); + return sptr; } @@ -137,7 +158,23 @@ Succeeded CListModeDataPETSIRD::get_next_record(CListRecord& record_of_general_type) const { CListRecordPETSIRD& record = dynamic_cast(record_of_general_type); - // return current_lm_data_ptr->get_next_record(record); + petsird::CoincidenceEvent& curr_event + = curr_event_block.prompt_events[type_of_module_pair[0]][type_of_module_pair[1]].at(curr_event_in_event_block); + + Succeeded ok = record.init_from_data_ptr(curr_event); + + if (ok == Succeeded::no) + return Succeeded::no; + + curr_event_in_event_block++; + if (curr_event_in_event_block == curr_event_block.prompt_events.size()) + { + if (!current_lm_data_ptr->ReadTimeBlocks(curr_time_block)) + return Succeeded::no; + curr_event_block = std::get(curr_time_block); + curr_event_in_event_block = 0; + } + return Succeeded::yes; } END_NAMESPACE_STIR diff --git a/src/listmode_buildblock/CMakeLists.txt b/src/listmode_buildblock/CMakeLists.txt index 20729259c6..bd65518987 100644 --- a/src/listmode_buildblock/CMakeLists.txt +++ b/src/listmode_buildblock/CMakeLists.txt @@ -75,10 +75,11 @@ if (HAVE_PETSIRD) #set(PETSIRD_dir ../../PETSIRD/cpp/generated) #add_subdirectory(${PETSIRD_dir} PETSIRD_generated) -# target_include_directories(listmode_buildblock PUBLIC -# $ -# $ -# ) +target_include_directories(listmode_buildblock PUBLIC + $ + $ + $ +) # target_include_directories(listmode_buildblock PUBLIC # $ From 4b39461e176b1f9fe86957f22e92f32ade9d15de Mon Sep 17 00:00:00 2001 From: NikEfth Date: Thu, 17 Jul 2025 22:02:33 -0400 Subject: [PATCH 23/42] Trying to figure out how the scanner looks like. --- .../stir/listmode/CListModeDataPETSIRD.h | 4 +- .../CListModeDataPETSIRD.cxx | 137 ++++++++++++++---- 2 files changed, 114 insertions(+), 27 deletions(-) diff --git a/src/include/stir/listmode/CListModeDataPETSIRD.h b/src/include/stir/listmode/CListModeDataPETSIRD.h index 3496070017..b7c7a0f8a6 100644 --- a/src/include/stir/listmode/CListModeDataPETSIRD.h +++ b/src/include/stir/listmode/CListModeDataPETSIRD.h @@ -40,8 +40,6 @@ Jannis Fischer #include "stir/listmode/CListRecord.h" #include "stir/shared_ptr.h" -#include "stir/listmode/CListRecordPETSIRD.h" - #include "../../PETSIRD/cpp/generated/protocols.h" START_NAMESPACE_STIR @@ -90,6 +88,8 @@ class CListModeDataPETSIRD : public CListModeDataBasedOnCoordinateMap mutable petsird::EventTimeBlock curr_event_block; const petsird::TypeOfModulePair type_of_module_pair{ 0, 0 }; + + shared_ptr this_scanner_sptr; }; END_NAMESPACE_STIR diff --git a/src/listmode_buildblock/CListModeDataPETSIRD.cxx b/src/listmode_buildblock/CListModeDataPETSIRD.cxx index dce71b7148..58b55e94ac 100644 --- a/src/listmode_buildblock/CListModeDataPETSIRD.cxx +++ b/src/listmode_buildblock/CListModeDataPETSIRD.cxx @@ -31,24 +31,21 @@ Coincidence LM Data Class for PETSIRD: Implementation \author Markus Jehl \author Daniel Deidda */ -#include -#include - #include "stir/ExamInfo.h" #include "stir/Succeeded.h" #include "stir/info.h" #include "stir/error.h" -#include "binary/protocols.h" -#include "helpers/include/petsird_helpers.h" -// #include "helpers/include/petsird_helpers/create.h" +#include "../../PETSIRD/cpp/helpers/include/petsird_helpers.h" +#include "helpers/include/petsird_helpers/create.h" #include "helpers/include/petsird_helpers/geometry.h" -// #include "boost/static_assert.hpp" + #include "../../PETSIRD/cpp/generated/binary/protocols.h" #include "../../PETSIRD/cpp/generated/hdf5/protocols.h" #include "stir/listmode/CListModeDataPETSIRD.h" #include "stir/listmode/CListRecordPETSIRD.h" +#include START_NAMESPACE_STIR @@ -73,8 +70,10 @@ CListModeDataPETSIRD::CListModeDataPETSIRD(const std::string& listmode_filename, // these are from rep_module.object // Get the first TimeBlock - if (current_lm_data_ptr->ReadTimeBlocks(curr_time_block)) - error("CListModeDataPETSIRD: Could not read the first TimeBlock. Abord."); + // if ( + current_lm_data_ptr->ReadTimeBlocks(curr_time_block); + // ) + // error("CListModeDataPETSIRD: Could not read the first TimeBlock. Abord."); if (std::holds_alternative(curr_time_block)) curr_event_block = std::get(curr_time_block); @@ -83,34 +82,121 @@ CListModeDataPETSIRD::CListModeDataPETSIRD(const std::string& listmode_filename, std::vector replicated_module_list = scanner_geo.replicated_modules; int num_modules = scanner_geo.replicated_modules[type_of_module].transforms.size(); + int num_elements_per_module = scanner_geo.replicated_modules[type_of_module].object.detecting_elements.transforms.size(); + const auto& tof_bin_edges = header.scanner.tof_bin_edges[type_of_module][type_of_module]; - const auto num_tof_bins = tof_bin_edges.NumberOfBins(); + // const auto num_tof_bins = tof_bin_edges.NumberOfBins(); const auto& event_energy_bin_edges = header.scanner.event_energy_bin_edges[type_of_module]; const auto num_event_energy_bins = event_energy_bin_edges.NumberOfBins(); // coordinates of first detecting bin (module_id,element_id, energy_id) const petsird::ExpandedDetectionBin expanded_detection_bin{ 0, 0, 0 }; - const auto box_shape = petsird_helpers::geometry::get_detecting_box(header.scanner, type_of_module, expanded_detection_bin); + // const auto box_shape = petsird_helpers::geometry::get_detecting_box(header.scanner, type_of_module, expanded_detection_bin); // get center of box (this should be in a loop to create a map + std::cout << scanner_info.gantry_alignment->matrix << std::endl; + float radius = 0; + + int dd = 0; + + double epsilon = 1e-6; + std::set unique_z_values; for (uint32_t module = 0; module < num_modules; module++) - for (uint32_t elem = 0; elem < num_elements_per_module; elem++) + { + petsird::RigidTransformation& mod_trans = scanner_geo.replicated_modules[type_of_module].transforms[module]; + + double tz = mod_trans.matrix.at(2, 3); // third row, fourth column + unique_z_values.insert(tz); + + std::cout << mod_trans.matrix << "\r" << std::endl; + } + + std::vector sorted_z(unique_z_values.begin(), unique_z_values.end()); + std::vector spacings; + for (size_t i = 1; i < sorted_z.size(); ++i) + { + spacings.push_back(std::abs(sorted_z[i] - sorted_z[i - 1])); + } + + double first = spacings.front(); + for (const auto& s : spacings) + { + if (std::abs(s - first) > epsilon) + error("Unequally spaced blocks. Probably. Abord."); + } + + std::cout << "I counted " << unique_z_values.size() << " axial number of blocks with spacing " << spacings[0] << std::endl; + int num_heads = num_modules / unique_z_values.size(); + std::cout << "I deduce that the scanner has " << num_heads << "transaxial number of blocks" << std::endl; + + for (uint32_t elem = 0; elem < num_elements_per_module; elem++) + { for (uint32_t ener = 0; ener < num_event_energy_bins; ener++) { - petsird::ExpandedDetectionBin expanded_detection_bin{ module, elem, ener }; - const auto box_shape - = petsird_helpers::geometry::get_detecting_box(header.scanner, type_of_module, expanded_detection_bin); - - CartesianCoordinate3D mean_pos; - for (auto& corner : box_shape.corners) - { // if STIR (z,y,x) -> PETSIRD (-y, -x, z) pheraps the order below needs to be changed - mean_pos.x() = +corner.c[0] / box_shape.corners.size(); - mean_pos.y() = +corner.c[1] / box_shape.corners.size(); - mean_pos.z() = +corner.c[2] / box_shape.corners.size(); - } - // save mean pos into map + + // const auto& rep_module = scanner.scanner_geometry.replicated_modules[type_of_module]; + // const auto& det_els = rep_module.object.detecting_elements; + // const auto& mod_transform = rep_module.transforms[expanded_detection_bin.module_index]; + // const auto& transform = det_els.transforms[expanded_detection_bin.element_index]; + // return transform_BoxShape(mult_transforms({ mod_transform, transform }), det_els.object.shape); + + // petsird::ExpandedDetectionBin expanded_detection_bin{ module, elem, ener }; + // const auto box_shape + // = petsird_helpers::geometry::get_detecting_box(header.scanner, type_of_module, expanded_detection_bin); + + petsird::RigidTransformation& el_trans + = scanner_geo.replicated_modules[type_of_module].object.detecting_elements.transforms[elem]; // .transforms[module]; + if (radius == 0) + radius = el_trans.matrix.at(0, 3); + else if (radius != el_trans.matrix.at(0, 3)) + error("Unsupported mixed radii. Abord."); + + dd++; } + } + + int nikos = 0; + + // this_scanner_sptr.reset(new Scanner(Scanner::User_defined_scanner, + // std::string("PETSIRD_defined_scanner"), + // /* num dets per ring */ + // num_modules, + // /* num of rings */ + // num_elements_per_module, + // /* number of non arccor bins */ + // num_modules / 2, + // /* number of maximum arccor bins */ + // this->default_num_arccorrected_bins, + // /* inner ring radius */ + // radius, + // /* doi */ 0.1F, + // /* 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)); // petsird::ReplicatedDetectorModule = scanner_geo.replicated_modules; @@ -148,7 +234,8 @@ shared_ptr CListModeDataPETSIRD::get_empty_record_sptr() const { shared_ptr sptr(new CListRecordPETSIRD); - std::dynamic_pointer_cast(sptr)->event_PETSIRD().set_scanner_sptr(this->get_proj_data_info_sptr()->get_scanner_sptr()); + std::dynamic_pointer_cast(sptr)->event_PETSIRD().set_scanner_sptr( + this->get_proj_data_info_sptr()->get_scanner_sptr()); std::dynamic_pointer_cast(sptr)->event_PETSIRD().set_map_sptr(map); return sptr; From 5499245443d98cbd4fbb181b09dd7126847e16ef Mon Sep 17 00:00:00 2001 From: NikEfth Date: Fri, 18 Jul 2025 14:18:12 -0400 Subject: [PATCH 24/42] Create ProjDataInfoBlocksOnCylindricalNoArcCorr from PETSIRD --- .../CListModeDataPETSIRD.cxx | 245 +++++++++++++----- 1 file changed, 175 insertions(+), 70 deletions(-) diff --git a/src/listmode_buildblock/CListModeDataPETSIRD.cxx b/src/listmode_buildblock/CListModeDataPETSIRD.cxx index 58b55e94ac..148c610dd7 100644 --- a/src/listmode_buildblock/CListModeDataPETSIRD.cxx +++ b/src/listmode_buildblock/CListModeDataPETSIRD.cxx @@ -86,6 +86,31 @@ CListModeDataPETSIRD::CListModeDataPETSIRD(const std::string& listmode_filename, int num_elements_per_module = scanner_geo.replicated_modules[type_of_module].object.detecting_elements.transforms.size(); const auto& tof_bin_edges = header.scanner.tof_bin_edges[type_of_module][type_of_module]; + std::cout << "Num. of TOF bins " << tof_bin_edges.NumberOfBins() << std::endl; + + std::cout << header.scanner.tof_resolution.size() << " " << header.scanner.tof_resolution[0].size() << std::endl; + + std::set unique_tof_values; + for (int module_type1 = 0; module_type1 < type_of_module; module_type1++) + { + std::cout << header.scanner.tof_resolution[module_type1].at(0) << std::endl; + unique_tof_values.insert(header.scanner.tof_resolution[module_type1].at(0)); + } + + if (unique_tof_values.size() > 1) + error("We do not support multiple TOF resolutions. Abord."); + + // + // for (uint32_t tof_bin = 0; tof_bin < tof_bin_edges.NumberOfBins(); module++) + // { + + // auto& tt = header.scanner.tof_resolution + // // unique_z_values.insert(tz); + + // std::cout + // << mod_trans.matrix << "\r" << std::endl; + // } + // const auto num_tof_bins = tof_bin_edges.NumberOfBins(); const auto& event_energy_bin_edges = header.scanner.event_energy_bin_edges[type_of_module]; const auto num_event_energy_bins = event_energy_bin_edges.NumberOfBins(); @@ -96,20 +121,22 @@ CListModeDataPETSIRD::CListModeDataPETSIRD(const std::string& listmode_filename, // get center of box (this should be in a loop to create a map std::cout << scanner_info.gantry_alignment->matrix << std::endl; - float radius = 0; - - int dd = 0; double epsilon = 1e-6; std::set unique_z_values; + std::set unique_tof_resolutions; + std::set unique_angle_modules; for (uint32_t module = 0; module < num_modules; module++) { petsird::RigidTransformation& mod_trans = scanner_geo.replicated_modules[type_of_module].transforms[module]; - - double tz = mod_trans.matrix.at(2, 3); // third row, fourth column + double tz = mod_trans.matrix.at(2, 3); unique_z_values.insert(tz); - - std::cout << mod_trans.matrix << "\r" << std::endl; + // std::cout << mod_trans.matrix << "\r" << std::endl; + unique_angle_modules.insert( + std::fabs(int(1000.F * std::atan2(mod_trans.matrix.at(1, 0), mod_trans.matrix.at(0, 0))) / 1000.F)); + std::cout << "Angular blocks " + << std::fabs(int(1000.F * std::atan2(mod_trans.matrix.at(1, 0), mod_trans.matrix.at(0, 0))) / 1000.F) + << std::endl; } std::vector sorted_z(unique_z_values.begin(), unique_z_values.end()); @@ -129,74 +156,151 @@ CListModeDataPETSIRD::CListModeDataPETSIRD(const std::string& listmode_filename, std::cout << "I counted " << unique_z_values.size() << " axial number of blocks with spacing " << spacings[0] << std::endl; int num_heads = num_modules / unique_z_values.size(); std::cout << "I deduce that the scanner has " << num_heads << "transaxial number of blocks" << std::endl; - + float radius = 0; + std::set unique_elements_y_values; + std::set unique_elements_z_values; for (uint32_t elem = 0; elem < num_elements_per_module; elem++) { - for (uint32_t ener = 0; ener < num_event_energy_bins; ener++) + // const auto& rep_module = scanner.scanner_geometry.replicated_modules[type_of_module]; + // const auto& det_els = rep_module.object.detecting_elements; + // const auto& mod_transform = rep_module.transforms[expanded_detection_bin.module_index]; + // const auto& transform = det_els.transforms[expanded_detection_bin.element_index]; + // return transform_BoxShape(mult_transforms({ mod_transform, transform }), det_els.object.shape); + + // petsird::ExpandedDetectionBin expanded_detection_bin{ module, elem, ener }; + // const auto box_shape + // = petsird_helpers::geometry::get_detecting_box(header.scanner, type_of_module, expanded_detection_bin); + + petsird::RigidTransformation& el_trans + = scanner_geo.replicated_modules[type_of_module].object.detecting_elements.transforms[elem]; // .transforms[module]; + if (radius == 0) + radius = el_trans.matrix.at(0, 3); + else if (radius != el_trans.matrix.at(0, 3)) + error("Unsupported mixed radii. Abord."); + + unique_elements_y_values.insert(el_trans.matrix.at(1, 3)); + unique_elements_z_values.insert(el_trans.matrix.at(2, 3)); + } + + std::vector sorted_angles_modules(unique_angle_modules.begin(), unique_angle_modules.end()); + std::vector spacing_angles; + for (size_t i = 1; i < sorted_angles_modules.size(); ++i) + { + spacing_angles.push_back(std::abs(sorted_angles_modules[i] - sorted_angles_modules[i - 1])); + } + + double first_angle = spacing_angles.front(); + for (const auto& s : spacing_angles) + { + if (std::abs(s - first_angle) > epsilon * 10000) // relax epsilon here { + std::cout << std::abs(s - first_angle) << std::endl; + error("Unequally spaced blocks. Probably. Abord."); + } + } - // const auto& rep_module = scanner.scanner_geometry.replicated_modules[type_of_module]; - // const auto& det_els = rep_module.object.detecting_elements; - // const auto& mod_transform = rep_module.transforms[expanded_detection_bin.module_index]; - // const auto& transform = det_els.transforms[expanded_detection_bin.element_index]; - // return transform_BoxShape(mult_transforms({ mod_transform, transform }), det_els.object.shape); + for (auto& e : unique_elements_y_values) + std::cout << "Y: " << e << std::endl; - // petsird::ExpandedDetectionBin expanded_detection_bin{ module, elem, ener }; - // const auto box_shape - // = petsird_helpers::geometry::get_detecting_box(header.scanner, type_of_module, expanded_detection_bin); + std::vector sorted_elements_y(unique_elements_y_values.begin(), unique_elements_y_values.end()); + std::vector element_y_spacings; + for (size_t i = 1; i < sorted_elements_y.size(); ++i) + { + element_y_spacings.push_back(std::abs(sorted_elements_y[i] - sorted_elements_y[i - 1])); + } - petsird::RigidTransformation& el_trans - = scanner_geo.replicated_modules[type_of_module].object.detecting_elements.transforms[elem]; // .transforms[module]; - if (radius == 0) - radius = el_trans.matrix.at(0, 3); - else if (radius != el_trans.matrix.at(0, 3)) - error("Unsupported mixed radii. Abord."); + double element_y_first = element_y_spacings.front(); + for (const auto& s : element_y_spacings) + { + if (std::abs(s - element_y_first) > epsilon) + error("Unequally spaced elements on the y axis. Probably. Abord."); + } - dd++; - } + for (auto& e : unique_elements_z_values) + std::cout << "Z: " << e << std::endl; + + std::vector sorted_elements_z(unique_elements_z_values.begin(), unique_elements_z_values.end()); + std::vector element_z_spacings; + for (size_t i = 1; i < sorted_elements_z.size(); ++i) + { + element_z_spacings.push_back(std::abs(sorted_elements_z[i] - sorted_elements_z[i - 1])); } - int nikos = 0; + double element_z_first = element_z_spacings.front(); + for (const auto& s : element_z_spacings) + { + if (std::abs(s - element_z_first) > epsilon) + error("Unequally spaced elements on the z axis. Probably. Abord."); + } - // this_scanner_sptr.reset(new Scanner(Scanner::User_defined_scanner, - // std::string("PETSIRD_defined_scanner"), - // /* num dets per ring */ - // num_modules, - // /* num of rings */ - // num_elements_per_module, - // /* number of non arccor bins */ - // num_modules / 2, - // /* number of maximum arccor bins */ - // this->default_num_arccorrected_bins, - // /* inner ring radius */ - // radius, - // /* doi */ 0.1F, - // /* 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)); + std::cout << "The scanner radius should be " << radius << std::endl; + + std::cout << (num_heads * unique_elements_y_values.size()) << "\r" + << (num_heads * unique_elements_y_values.size()) / unique_elements_y_values.size() + << "\r" // get_num_transaxial_blocks + << unique_elements_y_values.size() << std::endl; + + this_scanner_sptr.reset(new Scanner(Scanner::User_defined_scanner, + std::string("PETSIRD_defined_scanner"), + /* num dets per ring */ + (num_heads * unique_elements_y_values.size()), + unique_z_values.size() * unique_elements_z_values.size() /* num of rings */, + /* number of non arccor bins */ + (num_heads * unique_elements_y_values.size()) / 2, + /* number of maximum arccor bins */ + (num_heads * unique_elements_y_values.size()) / 2, + /* inner ring radius */ + radius, + /* doi */ 1.F, + /* ring spacing */ + element_z_spacings[0] * 10.f, + // bin_size_v + element_y_spacings[0] * 10.f, + /*intrinsic_tilt_v*/ + 0.f, + /*num_axial_blocks_per_bucket_v */ + unique_z_values.size(), + /*num_transaxial_blocks_per_bucket_v*/ + 1, + /*num_axial_crystals_per_block_v*/ + unique_elements_z_values.size(), + /*num_transaxial_crystals_per_block_v*/ + unique_elements_y_values.size(), + /*num_axial_crystals_per_singles_unit_v*/ + unique_elements_z_values.size(), + /*num_transaxial_crystals_per_singles_unit_v*/ + unique_elements_y_values.size(), + /*num_detector_layers_v*/ + 1, // num_detector_layers_v + -1, // energy_resolution_v + -1, // reference_energy_v + 1, + 0.F, + 0.F, // non-TOF + "BlocksOnCylindrical", // scanner_geometry_v + element_z_spacings[0], // axial_crystal_spacing_v + std::round(element_y_spacings[0] * 10.0f) / 10.F, // transaxial_crystal_spacing_v + spacings[0], // axial_block_spacing_v + radius * spacing_angles.front(), // transaxial_block_spacing_v + "" // crystal_map_file_name_v + )); + + // /* maximum number of timing bins */ + // tof_bin_edges.NumberOfBins(), + // /* size of basic TOF bin */ + // 10, + // /* Scanner's timing resolution */ + // *unique_tof_values.begin())); + int tof_mash_factor = 1; + 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()); // petsird::ReplicatedDetectorModule = scanner_geo.replicated_modules; @@ -217,16 +321,17 @@ CListModeDataPETSIRD::CListModeDataPETSIRD(const std::string& listmode_filename, // 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 (this->open_lm_file() == Succeeded::no) - { - error("CListModeDataPETSIRD: Could not open listmode file " + listmode_filename + "\n"); - } + // if (this->open_lm_file() == Succeeded::no) + // { + // error("CListModeDataPETSIRD: Could not open listmode file " + listmode_filename + "\n"); + // } + int nikos = 0; } Succeeded CListModeDataPETSIRD::open_lm_file() const { - current_lm_data_ptr.reset(new petsird::hdf5::PETSIRDReader(listmode_filename)); + // current_lm_data_ptr.reset(new petsird::hdf5::PETSIRDReader(listmode_filename)); return Succeeded::yes; } From d00e98755e0c4e36918e7183855574c2ec8518d7 Mon Sep 17 00:00:00 2001 From: NikEfth Date: Tue, 22 Jul 2025 11:10:56 -0400 Subject: [PATCH 25/42] * Clean up the code that creates a Scanner object from PETSRID header information. --- .../stir/listmode/CListModeDataPETSIRD.h | 34 +- .../CListModeDataPETSIRD.cxx | 595 +++++++++++------- 2 files changed, 386 insertions(+), 243 deletions(-) diff --git a/src/include/stir/listmode/CListModeDataPETSIRD.h b/src/include/stir/listmode/CListModeDataPETSIRD.h index b7c7a0f8a6..c65f34389e 100644 --- a/src/include/stir/listmode/CListModeDataPETSIRD.h +++ b/src/include/stir/listmode/CListModeDataPETSIRD.h @@ -1,11 +1,8 @@ /* CListModeDataPETSIRD.h -Coincidence LM Data Class for PETSIRD: Header File -Jannis Fischer +Coincidence LM Data Class for PETSIRD - Copyright 2015 ETH Zurich, Institute of Particle Physics - Copyright 2020 Positrigo AG, Zurich - Copyright 2025 National Physical Laboratory + Copyright 2025, MGH / HST A. Martinos Center for Biomedical Imaging Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. @@ -28,15 +25,15 @@ Jannis Fischer \brief Declaration of class stir::CListModeDataPETSIRD \author Daniel Deidda +\author Nikos Efthimiou */ #ifndef __stir_listmode_CListModeDataPETSIRD_H__ #define __stir_listmode_CListModeDataPETSIRD_H__ -#include +#include #include "stir/listmode/CListModeDataBasedOnCoordinateMap.h" #include "stir/ProjData.h" -#include "stir/ProjDataInfo.h" #include "stir/listmode/CListRecord.h" #include "stir/shared_ptr.h" @@ -90,6 +87,29 @@ class CListModeDataPETSIRD : public CListModeDataBasedOnCoordinateMap const petsird::TypeOfModulePair type_of_module_pair{ 0, 0 }; shared_ptr this_scanner_sptr; + + bool isCylindricalConfiguration(const petsird::ScannerInformation& scanner_info, + const std::vector& replicated_module_list); + + void find_uniqe_values_1D(std::set& values, const std::vector& input); + + void find_uniqe_values_2D(std::set& values, const std::vector>& input); + + int figure_out_scanner_blocks_and_rotation_axis(std::set& unique_dim1_values, + std::set& unique_dim2_values, + std::set& unique_dim3_values, + const std::vector& replicated_module_list); + void figure_out_block_element_transformations(std::set& unique_dim1_values, + std::set& unique_dim2_values, + std::set& unique_dim3_values, + float& radius, + int& radius_index, + const int rotation_axis, + const std::vector& replicated_module_list); + + void figure_out_block_angles(std::set& unique_angle_modules, + const int rot_axis, + const std::vector& replicated_module_list); }; END_NAMESPACE_STIR diff --git a/src/listmode_buildblock/CListModeDataPETSIRD.cxx b/src/listmode_buildblock/CListModeDataPETSIRD.cxx index 148c610dd7..995afb38ee 100644 --- a/src/listmode_buildblock/CListModeDataPETSIRD.cxx +++ b/src/listmode_buildblock/CListModeDataPETSIRD.cxx @@ -2,11 +2,7 @@ Coincidence LM Data Class for PETSIRD: Implementation - Copyright 2015 ETH Zurich, Institute of Particle Physics - Copyright 2020 Positrigo AG, Zurich - Copyright 2021 University College London - Copyright 2025 National Physical Laboratory - + Copyright 2025, MGH / HST A. Martinos Center for Biomedical Imaging Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. @@ -26,12 +22,10 @@ Coincidence LM Data Class for PETSIRD: Implementation \ingroup listmode \brief implementation of class stir::CListModeDataPETSIRD -\author Jannis Fischer -\author Kris Thielemans -\author Markus Jehl \author Daniel Deidda +\author Nikos Efthimiou */ -#include "stir/ExamInfo.h" + #include "stir/Succeeded.h" #include "stir/info.h" #include "stir/error.h" @@ -49,241 +43,352 @@ Coincidence LM Data Class for PETSIRD: Implementation START_NAMESPACE_STIR -CListModeDataPETSIRD::CListModeDataPETSIRD(const std::string& listmode_filename, bool use_hdf5) - : use_hdf5(use_hdf5) +namespace matrix { - this->listmode_filename = listmode_filename; - - petsird::Header header; - if (use_hdf5) - current_lm_data_ptr.reset(new petsird::hdf5::PETSIRDReader(listmode_filename)); - else - current_lm_data_ptr.reset(new petsird::binary::PETSIRDReader(listmode_filename)); - - current_lm_data_ptr->ReadHeader(header); - petsird::ScannerInformation scanner_info = header.scanner; - petsird::ScannerGeometry scanner_geo = scanner_info.scanner_geometry; - const petsird::TypeOfModule type_of_module{ 0 }; - // need to get inner_ring_radius, num_detector_layers, num_transaxial_crystals_per_block, num_axial_crystals_per_block - // transaxial_crystal_spacing,average_depth_of_interaction,axial_crystal_spacing, num_rings, ring_spacing, num_axial_blocks, - // num_oftransaxial_blocks - // these are from rep_module.object - // Get the first TimeBlock - // if ( - current_lm_data_ptr->ReadTimeBlocks(curr_time_block); - // ) - // error("CListModeDataPETSIRD: Could not read the first TimeBlock. Abord."); +using Mat3 = std::array, 3>; +using Vec3 = std::array; - if (std::holds_alternative(curr_time_block)) - curr_event_block = std::get(curr_time_block); - else - error("CListModeDataPETSIRD: holds_alternative not true. Abord."); - - std::vector replicated_module_list = scanner_geo.replicated_modules; - int num_modules = scanner_geo.replicated_modules[type_of_module].transforms.size(); +inline Mat3 +transpose(const Mat3& mat) +{ + std::array, 3> result{}; + for (int i = 0; i < 3; ++i) + for (int j = 0; j < 3; ++j) + result[j][i] = mat[i][j]; + return result; +} - int num_elements_per_module = scanner_geo.replicated_modules[type_of_module].object.detecting_elements.transforms.size(); +inline Mat3 +subtract(const Mat3& A, const Mat3& B) +{ + std::array, 3> result{}; + for (size_t i = 0; i < 3; ++i) + for (size_t j = 0; j < 3; ++j) + result[i][j] = A[i][j] - B[i][j]; + return result; +} - const auto& tof_bin_edges = header.scanner.tof_bin_edges[type_of_module][type_of_module]; - std::cout << "Num. of TOF bins " << tof_bin_edges.NumberOfBins() << std::endl; +inline Vec3 +getAxisFromSkew(const Mat3& S) +{ + return { + 0.5f * (S[2][1] - S[1][2]), // x + 0.5f * (S[0][2] - S[2][0]), // y + 0.5f * (S[1][0] - S[0][1]) // z + }; +} - std::cout << header.scanner.tof_resolution.size() << " " << header.scanner.tof_resolution[0].size() << std::endl; +} // namespace matrix - std::set unique_tof_values; - for (int module_type1 = 0; module_type1 < type_of_module; module_type1++) +inline bool +get_spacing_uniform(std::vector& spacing, const std::set& unsorted_block_poss, double epsilon = 1e-4) +{ + std::vector sorted_z(unsorted_block_poss.begin(), unsorted_block_poss.end()); + for (size_t i = 1; i < sorted_z.size(); ++i) { - std::cout << header.scanner.tof_resolution[module_type1].at(0) << std::endl; - unique_tof_values.insert(header.scanner.tof_resolution[module_type1].at(0)); + spacing.push_back(std::abs(sorted_z[i] - sorted_z[i - 1])); } - if (unique_tof_values.size() > 1) - error("We do not support multiple TOF resolutions. Abord."); - - // - // for (uint32_t tof_bin = 0; tof_bin < tof_bin_edges.NumberOfBins(); module++) - // { - - // auto& tt = header.scanner.tof_resolution - // // unique_z_values.insert(tz); - - // std::cout - // << mod_trans.matrix << "\r" << std::endl; - // } - - // const auto num_tof_bins = tof_bin_edges.NumberOfBins(); - const auto& event_energy_bin_edges = header.scanner.event_energy_bin_edges[type_of_module]; - const auto num_event_energy_bins = event_energy_bin_edges.NumberOfBins(); - // coordinates of first detecting bin (module_id,element_id, energy_id) - const petsird::ExpandedDetectionBin expanded_detection_bin{ 0, 0, 0 }; - // const auto box_shape = petsird_helpers::geometry::get_detecting_box(header.scanner, type_of_module, expanded_detection_bin); - - // get center of box (this should be in a loop to create a map - - std::cout << scanner_info.gantry_alignment->matrix << std::endl; + return std::all_of(spacing.begin(), spacing.end(), [&](float s) { return std::abs(s - spacing.front()) <= epsilon; }); +} - double epsilon = 1e-6; - std::set unique_z_values; - std::set unique_tof_resolutions; - std::set unique_angle_modules; - for (uint32_t module = 0; module < num_modules; module++) +const std::set& +getLargestVector(const std::set& x, const std::set& y, const std::set& z) +{ + const std::set* largest = &x; + int axis = 0; + if (y.size() > largest->size()) { - petsird::RigidTransformation& mod_trans = scanner_geo.replicated_modules[type_of_module].transforms[module]; - double tz = mod_trans.matrix.at(2, 3); - unique_z_values.insert(tz); - // std::cout << mod_trans.matrix << "\r" << std::endl; - unique_angle_modules.insert( - std::fabs(int(1000.F * std::atan2(mod_trans.matrix.at(1, 0), mod_trans.matrix.at(0, 0))) / 1000.F)); - std::cout << "Angular blocks " - << std::fabs(int(1000.F * std::atan2(mod_trans.matrix.at(1, 0), mod_trans.matrix.at(0, 0))) / 1000.F) - << std::endl; + largest = &y; + axis = 1; } - - std::vector sorted_z(unique_z_values.begin(), unique_z_values.end()); - std::vector spacings; - for (size_t i = 1; i < sorted_z.size(); ++i) + else if (z.size() > largest->size()) { - spacings.push_back(std::abs(sorted_z[i] - sorted_z[i - 1])); + largest = &z; + axis = 2; } - double first = spacings.front(); - for (const auto& s : spacings) - { - if (std::abs(s - first) > epsilon) - error("Unequally spaced blocks. Probably. Abord."); - } + info(format("I believe the axial direction is the {}.", axis)); + return *largest; +} - std::cout << "I counted " << unique_z_values.size() << " axial number of blocks with spacing " << spacings[0] << std::endl; - int num_heads = num_modules / unique_z_values.size(); - std::cout << "I deduce that the scanner has " << num_heads << "transaxial number of blocks" << std::endl; - float radius = 0; - std::set unique_elements_y_values; - std::set unique_elements_z_values; - for (uint32_t elem = 0; elem < num_elements_per_module; elem++) +void +CListModeDataPETSIRD::find_uniqe_values_1D(std::set& values, const std::vector& input) +{ + for (float val : input) { - // const auto& rep_module = scanner.scanner_geometry.replicated_modules[type_of_module]; - // const auto& det_els = rep_module.object.detecting_elements; - // const auto& mod_transform = rep_module.transforms[expanded_detection_bin.module_index]; - // const auto& transform = det_els.transforms[expanded_detection_bin.element_index]; - // return transform_BoxShape(mult_transforms({ mod_transform, transform }), det_els.object.shape); - - // petsird::ExpandedDetectionBin expanded_detection_bin{ module, elem, ener }; - // const auto box_shape - // = petsird_helpers::geometry::get_detecting_box(header.scanner, type_of_module, expanded_detection_bin); - - petsird::RigidTransformation& el_trans - = scanner_geo.replicated_modules[type_of_module].object.detecting_elements.transforms[elem]; // .transforms[module]; - if (radius == 0) - radius = el_trans.matrix.at(0, 3); - else if (radius != el_trans.matrix.at(0, 3)) - error("Unsupported mixed radii. Abord."); - - unique_elements_y_values.insert(el_trans.matrix.at(1, 3)); - unique_elements_z_values.insert(el_trans.matrix.at(2, 3)); + // std::cout << val << std::endl; + values.insert(val); } +} - std::vector sorted_angles_modules(unique_angle_modules.begin(), unique_angle_modules.end()); - std::vector spacing_angles; - for (size_t i = 1; i < sorted_angles_modules.size(); ++i) - { - spacing_angles.push_back(std::abs(sorted_angles_modules[i] - sorted_angles_modules[i - 1])); - } +void +CListModeDataPETSIRD::find_uniqe_values_2D(std::set& values, const std::vector>& input) +{ + for (size_t row = 0; row < input.size(); ++row) + for (size_t col = 0; col < input[row].size(); ++col) + values.insert(input[row][col]); +} - double first_angle = spacing_angles.front(); - for (const auto& s : spacing_angles) +int +CListModeDataPETSIRD::figure_out_scanner_blocks_and_rotation_axis( + std::set& unique_dim1_values, + std::set& unique_dim2_values, + std::set& unique_dim3_values, + const std::vector& replicated_module_list) +{ + auto insertTranslations = [&](const petsird::RigidTransformation& trans) { + unique_dim1_values.insert(trans.matrix.at(0, 3)); + unique_dim2_values.insert(trans.matrix.at(1, 3)); + unique_dim3_values.insert(trans.matrix.at(2, 3)); + }; + + auto extractRotationMatrix = [](const petsird::RigidTransformation& trans) -> matrix::Mat3 { + matrix::Mat3 R; + for (int i = 0; i < 3; ++i) + for (int j = 0; j < 3; ++j) + R[i][j] = trans.matrix.at(i, j); + return R; + // skew = matrix::subtract(R, matrix::transpose(R)); + // auto rot = matrix::getAxisFromSkew(skew); + }; + + std::array, 3> skew; + int detected_axis = -1; + + for (const auto& module : replicated_module_list) + for (const auto& mod_trans : module.transforms) + { + insertTranslations(mod_trans); + matrix::Mat3 R = extractRotationMatrix(mod_trans); + skew = matrix::subtract(R, matrix::transpose(R)); + auto axis_vec = matrix::getAxisFromSkew(skew); + + int current_axis = -1; + for (int i = 0; i < 3; ++i) + { + if (std::abs(axis_vec[i]) > 1e-6f) + { + if (current_axis != -1) + { + warning("Rotation involves multiple axis components. Possibly non-pure rotation."); + current_axis = -2; // Sentinel for mixed axes + return -1; + } + current_axis = i; + } + } + + if (current_axis >= 0) + { + if (detected_axis == -1) + detected_axis = current_axis; + else if (detected_axis != current_axis) + warning("Inconsistent rotation axis detected between modules."); + } + } + info(format("Rotation axis of blocks inferred as axis index {}", detected_axis)); + return detected_axis; +} + +void +CListModeDataPETSIRD::figure_out_block_element_transformations( + std::set& unique_dim1_values, + std::set& unique_dim2_values, + std::set& unique_dim3_values, + float& radius, + int& radius_index, + const int rotation_axis, + const std::vector& replicated_module_list) +{ + + auto insert_translation = [&](const petsird::RigidTransformation& trans) { + unique_dim1_values.insert(trans.matrix.at(0, 3)); + unique_dim2_values.insert(trans.matrix.at(1, 3)); + unique_dim3_values.insert(trans.matrix.at(2, 3)); + }; + + auto detect_radius = [&](const petsird::RigidTransformation& trans) -> bool { + for (int i = 0; i < 3; ++i) + { + if (i == rotation_axis) + continue; + float candidate = trans.matrix.at(i, 3); + if (candidate > 0.0f) + { + radius = candidate; + radius_index = i; + return true; + } + } + return false; + }; + + for (const auto& module : replicated_module_list) { - if (std::abs(s - first_angle) > epsilon * 10000) // relax epsilon here + for (const auto& el_trans : module.object.detecting_elements.transforms) { - std::cout << std::abs(s - first_angle) << std::endl; - error("Unequally spaced blocks. Probably. Abord."); + if (radius == 0.0f) + { + if (!detect_radius(el_trans)) + { + error("Unable to determine radius from translation components."); + continue; + } + } + else + { + float current = el_trans.matrix.at(radius_index, 3); + if (std::abs(current - radius) > 1e-4f) + warning("Mixed radii detected. Consider checking for misaligned modules."); + } + + insert_translation(el_trans); + // std::cout << el_trans.matrix << std::endl; } } +} - for (auto& e : unique_elements_y_values) - std::cout << "Y: " << e << std::endl; +void +CListModeDataPETSIRD::figure_out_block_angles(std::set& unique_angle_modules, + const int rot_axis, + const std::vector& replicated_module_list) +{ + for (const auto& module : replicated_module_list) + for (const auto& transform : module.transforms) + { + if (rot_axis == 0) + unique_angle_modules.insert( + std::fabs(int(1000.F * std::atan2(transform.matrix.at(1, 0), transform.matrix.at(2, 0))) / 1000.F)); + else if (rot_axis == 1) + unique_angle_modules.insert( + std::fabs(int(1000.F * std::atan2(transform.matrix.at(2, 0), transform.matrix.at(0, 0))) / 1000.F)); + else if (rot_axis == 2) + unique_angle_modules.insert( + std::fabs(int(1000.F * std::atan2(transform.matrix.at(1, 0), transform.matrix.at(0, 0))) / 1000.F)); + } +} - std::vector sorted_elements_y(unique_elements_y_values.begin(), unique_elements_y_values.end()); - std::vector element_y_spacings; - for (size_t i = 1; i < sorted_elements_y.size(); ++i) - { - element_y_spacings.push_back(std::abs(sorted_elements_y[i] - sorted_elements_y[i - 1])); - } +bool +CListModeDataPETSIRD::isCylindricalConfiguration(const petsird::ScannerInformation& scanner_info, + const std::vector& replicated_module_list) +{ + // Determine DOI based on material + const std::string& material = scanner_info.bulk_materials[0].name; + float average_doi = (material == "BGO") ? 5.0f : (material == "LSO" || material == "LYSO") ? 7.0f : 0.0f; - double element_y_first = element_y_spacings.front(); - for (const auto& s : element_y_spacings) + const petsird::TypeOfModule type_of_module = replicated_module_list.size(); + if (type_of_module > 1) { - if (std::abs(s - element_y_first) > epsilon) - error("Unequally spaced elements on the y axis. Probably. Abord."); + info("Multiple types of PETSIRD modules are not supported. Abord."); + return false; } - for (auto& e : unique_elements_z_values) - std::cout << "Z: " << e << std::endl; + const auto& tof_bin_edges = scanner_info.tof_bin_edges[type_of_module - 1][type_of_module - 1]; + std::cout << "Num. of TOF bins " << tof_bin_edges.NumberOfBins() << std::endl; + + std::set unique_tof_values; + find_uniqe_values_2D(unique_tof_values, scanner_info.tof_resolution); + + int num_modules = replicated_module_list[0].transforms.size(); + std::set unique_dim1_values, unique_dim2_values, unique_dim3_values; + std::set unique_tof_resolutions; + + const int rotation_axis = figure_out_scanner_blocks_and_rotation_axis( + unique_dim1_values, unique_dim2_values, unique_dim3_values, replicated_module_list); + if (rotation_axis == -1) + return false; + + const std::set& main_axis = getLargestVector(unique_dim1_values, unique_dim2_values, unique_dim3_values); + + std::vector block_axial_spacing; + get_spacing_uniform(block_axial_spacing, main_axis); - std::vector sorted_elements_z(unique_elements_z_values.begin(), unique_elements_z_values.end()); - std::vector element_z_spacings; - for (size_t i = 1; i < sorted_elements_z.size(); ++i) + info(format("I counted {} axial blocks with spacing {}", unique_dim3_values.size(), block_axial_spacing[0])); + + int num_transaxial_blocks = num_modules / main_axis.size(); + info(format("I deduce that the scanner has {} transaxial number of blocks", num_transaxial_blocks)); + + float radius = 0; + int radius_indx = -1; + + std::set unique_elements_dim1_values, unique_elements_dim2_values, unique_elements_dim3_values; + figure_out_block_element_transformations(unique_elements_dim1_values, + unique_elements_dim2_values, + unique_elements_dim3_values, + radius, + radius_indx, + rotation_axis, + replicated_module_list); + std::set unique_angle_modules; + figure_out_block_angles(unique_angle_modules, rotation_axis, replicated_module_list); + + std::vector block_angular_spacing; + if (!get_spacing_uniform(block_angular_spacing, unique_angle_modules)) /// epsilon * 10000) // relax epsilon here + return false; + + std::vector element_horizontal_spacing, element_vertical_spacing; + std::set unique_elements_horizontal_values, unique_elements_vertical_values; + if (radius_indx == 0) { - element_z_spacings.push_back(std::abs(sorted_elements_z[i] - sorted_elements_z[i - 1])); + if (!get_spacing_uniform(element_horizontal_spacing, unique_elements_dim3_values)) + return false; + if (!get_spacing_uniform(element_vertical_spacing, unique_elements_dim2_values)) + return false; + unique_elements_horizontal_values = unique_elements_dim3_values; + unique_elements_vertical_values = unique_elements_dim2_values; } - - double element_z_first = element_z_spacings.front(); - for (const auto& s : element_z_spacings) + else { - if (std::abs(s - element_z_first) > epsilon) - error("Unequally spaced elements on the z axis. Probably. Abord."); + error("TODO!"); } - std::cout << "The scanner radius should be " << radius << std::endl; - - std::cout << (num_heads * unique_elements_y_values.size()) << "\r" - << (num_heads * unique_elements_y_values.size()) / unique_elements_y_values.size() - << "\r" // get_num_transaxial_blocks - << unique_elements_y_values.size() << std::endl; - - this_scanner_sptr.reset(new Scanner(Scanner::User_defined_scanner, - std::string("PETSIRD_defined_scanner"), - /* num dets per ring */ - (num_heads * unique_elements_y_values.size()), - unique_z_values.size() * unique_elements_z_values.size() /* num of rings */, - /* number of non arccor bins */ - (num_heads * unique_elements_y_values.size()) / 2, - /* number of maximum arccor bins */ - (num_heads * unique_elements_y_values.size()) / 2, - /* inner ring radius */ - radius, - /* doi */ 1.F, - /* ring spacing */ - element_z_spacings[0] * 10.f, - // bin_size_v - element_y_spacings[0] * 10.f, - /*intrinsic_tilt_v*/ - 0.f, - /*num_axial_blocks_per_bucket_v */ - unique_z_values.size(), - /*num_transaxial_blocks_per_bucket_v*/ - 1, - /*num_axial_crystals_per_block_v*/ - unique_elements_z_values.size(), - /*num_transaxial_crystals_per_block_v*/ - unique_elements_y_values.size(), - /*num_axial_crystals_per_singles_unit_v*/ - unique_elements_z_values.size(), - /*num_transaxial_crystals_per_singles_unit_v*/ - unique_elements_y_values.size(), - /*num_detector_layers_v*/ - 1, // num_detector_layers_v - -1, // energy_resolution_v - -1, // reference_energy_v - 1, - 0.F, - 0.F, // non-TOF - "BlocksOnCylindrical", // scanner_geometry_v - element_z_spacings[0], // axial_crystal_spacing_v - std::round(element_y_spacings[0] * 10.0f) / 10.F, // transaxial_crystal_spacing_v - spacings[0], // axial_block_spacing_v - radius * spacing_angles.front(), // transaxial_block_spacing_v - "" // crystal_map_file_name_v - )); + this_scanner_sptr.reset( + new Scanner(Scanner::User_defined_scanner, + std::string("PETSIRD_defined_scanner"), + /* num dets per ring */ + (num_transaxial_blocks * unique_elements_vertical_values.size()), + unique_dim3_values.size() * unique_elements_horizontal_values.size() /* num of rings */, + /* number of non arccor bins */ + (num_transaxial_blocks * unique_elements_vertical_values.size()) / 2, + /* number of maximum arccor bins */ + (num_transaxial_blocks * unique_elements_vertical_values.size()) / 2, + /* inner ring radius */ + radius, + /* doi */ average_doi, + /* ring spacing */ + element_horizontal_spacing[0] * 10.f, + // bin_size_v + element_vertical_spacing[0] * 10.f, + /*intrinsic_tilt_v*/ + 0.f, + /*num_axial_blocks_per_bucket_v */ + unique_dim3_values.size(), + /*num_transaxial_blocks_per_bucket_v*/ + 1, + /*num_axial_crystals_per_block_v*/ + unique_elements_horizontal_values.size(), + /*num_transaxial_crystals_per_block_v*/ + unique_elements_vertical_values.size(), + /*num_axial_crystals_per_singles_unit_v*/ + unique_elements_horizontal_values.size(), + /*num_transaxial_crystals_per_singles_unit_v*/ + unique_elements_vertical_values.size(), + /*num_detector_layers_v*/ + 1, // num_detector_layers_v + scanner_info.energy_resolution_at_511.front(), // energy_resolution_v + 511, // reference_energy_v + 1, + 0.F, + 0.F, // non-TOF + "BlocksOnCylindrical", // scanner_geometry_v + *unique_elements_horizontal_values.begin(), // axial_crystal_spacing_v + std::round(*unique_elements_vertical_values.begin() * 10.0f) / 10.F, // transaxial_crystal_spacing_v + block_axial_spacing.front(), // axial_block_spacing_v + radius * block_angular_spacing.front(), // transaxial_block_spacing_v + "" // crystal_map_file_name_v + )); // /* maximum number of timing bins */ // tof_bin_edges.NumberOfBins(), @@ -291,41 +396,59 @@ CListModeDataPETSIRD::CListModeDataPETSIRD(const std::string& listmode_filename, // 10, // /* Scanner's timing resolution */ // *unique_tof_values.begin())); - int tof_mash_factor = 1; - 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()); - - // petsird::ReplicatedDetectorModule = scanner_geo.replicated_modules; - - // if (!crystal_map_filename.empty()) - // { - // this->map = MAKE_SHARED(crystal_map_filename, lor_randomization_sigma); - // } - // else - // { - // if (lor_randomization_sigma != 0) - // error("PETSIRD currently does not support LOR-randomisation unless a map is specified"); - // } - // shared_ptr _exam_info_sptr(new ExamInfo); - // _exam_info_sptr->imaging_modality = ImagingModality::PT; - // this->exam_info_sptr = _exam_info_sptr; - // // 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()); + return true; +} + +CListModeDataPETSIRD::CListModeDataPETSIRD(const std::string& listmode_filename, bool use_hdf5) + : use_hdf5(use_hdf5) +{ + this->listmode_filename = listmode_filename; + + petsird::Header header; + if (use_hdf5) + current_lm_data_ptr.reset(new petsird::hdf5::PETSIRDReader(listmode_filename)); + else + current_lm_data_ptr.reset(new petsird::binary::PETSIRDReader(listmode_filename)); + + current_lm_data_ptr->ReadHeader(header); + petsird::ScannerInformation scanner_info = header.scanner; + petsird::ScannerGeometry scanner_geo = scanner_info.scanner_geometry; + std::vector replicated_module_list = scanner_geo.replicated_modules; + + // Get the first TimeBlock + // if ( + current_lm_data_ptr->ReadTimeBlocks(curr_time_block); + // ) + // error("CListModeDataPETSIRD: Could not read the first TimeBlock. Abord."); + + if (std::holds_alternative(curr_time_block)) + curr_event_block = std::get(curr_time_block); + else + error("CListModeDataPETSIRD: holds_alternative not true. Abord."); + + if (isCylindricalConfiguration(scanner_info, replicated_module_list)) + { + int tof_mash_factor = 1; + 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()); + } + else + { + error("TODO:GenericScanner"); + } // if (this->open_lm_file() == Succeeded::no) // { // error("CListModeDataPETSIRD: Could not open listmode file " + listmode_filename + "\n"); // } - int nikos = 0; } Succeeded From d0e46a4bb151a825cc5a585a22956248d59eb94b Mon Sep 17 00:00:00 2001 From: NikEfth Date: Tue, 22 Jul 2025 12:11:51 -0400 Subject: [PATCH 26/42] * Stop testing for PETSIRD --- .github/workflows/build-test.yml | 2 +- .travis.yml | 18 +++++++++--------- 2 files changed, 10 insertions(+), 10 deletions(-) diff --git a/.github/workflows/build-test.yml b/.github/workflows/build-test.yml index d5e65c32fb..25ffd88540 100644 --- a/.github/workflows/build-test.yml +++ b/.github/workflows/build-test.yml @@ -330,7 +330,7 @@ jobs: EXTRA_BUILD_FLAGS="${EXTRA_BUILD_FLAGS} -DBUILD_SWIG_PYTHON=ON -DPython_EXECUTABLE=`which python`" EXTRA_BUILD_FLAGS="${EXTRA_BUILD_FLAGS} -DCMAKE_INSTALL_PREFIX=${CMAKE_INSTALL_PREFIX} -DCMAKE_BUILD_TYPE=${BUILD_TYPE}" EXTRA_BUILD_FLAGS="${EXTRA_BUILD_FLAGS} -DDOWNLOAD_ZENODO_TEST_DATA=ON" - EXTRA_BUILD_FLAGS="${EXTRA_BUILD_FLAGS} -DDISABLE_STIR_LOCAL=OFF -DSTIR_LOCAL=${GITHUB_WORKSPACE}/examples/C++/using_STIR_LOCAL" + EXTRA_BUILD_FLAGS="${EXTRA_BUILD_FLAGS} -DDISABLE_PETSIRD:BOOL=ON -DDISABLE_STIR_LOCAL=OFF -DSTIR_LOCAL=${GITHUB_WORKSPACE}/examples/C++/using_STIR_LOCAL" echo "cmake flags $BUILD_FLAGS $EXTRA_BUILD_FLAGS" mkdir build cd build diff --git a/.travis.yml b/.travis.yml index 3d6b75b578..9ad59d8f03 100644 --- a/.travis.yml +++ b/.travis.yml @@ -35,7 +35,7 @@ matrix: packages: - *addons_apt_packages - [g++-6] - env: EXTRA_BUILD_FLAGS="-DDISABLE_CERN_ROOT=1 -DSTIR_OPENMP:BOOL=OFF" CC=gcc-6 CXX=g++-6 + env: EXTRA_BUILD_FLAGS="-DDISABLE_CERN_ROOT=1 -DSTIR_OPENMP:BOOL=OFF -DDISABLE_PETSIRD:BOOL=ON" CC=gcc-6 CXX=g++-6 - os: linux python: 3 addons: @@ -43,7 +43,7 @@ matrix: packages: - *addons_apt_packages - [g++-5, libinsighttoolkit4-dev] - env: EXTRA_BUILD_FLAGS="-DDISABLE_CERN_ROOT=1 -DSTIR_OPENMP:BOOL=OFF -DDISABLE_HDF5=ON" CC=gcc-5 CXX=g++-5 + env: EXTRA_BUILD_FLAGS="-DDISABLE_CERN_ROOT=1 -DSTIR_OPENMP:BOOL=OFF -DDISABLE_HDF5=ON -DDISABLE_PETSIRD:BOOL=ON" CC=gcc-5 CXX=g++-5 - os: linux python: 3 addons: @@ -51,7 +51,7 @@ matrix: packages: - *addons_apt_packages - [g++-7, libinsighttoolkit4-dev] - env: EXTRA_BUILD_FLAGS="-DDISABLE_CERN_ROOT=0 -DSTIR_OPENMP:BOOL=ON" CC=gcc-7 CXX=g++-7 + env: EXTRA_BUILD_FLAGS="-DDISABLE_CERN_ROOT=0 -DSTIR_OPENMP:BOOL=ON -DDISABLE_PETSIRD:BOOL=ON" CC=gcc-7 CXX=g++-7 - os: linux dist: focal python: 3 @@ -60,7 +60,7 @@ matrix: packages: - *addons_apt_packages - [g++-9, libinsighttoolkit4-dev, nlohmann-json3-dev] - env: EXTRA_BUILD_FLAGS="-DDISABLE_CERN_ROOT=0 -DSTIR_OPENMP:BOOL=ON" CC=gcc-9 CXX=g++-9 + env: EXTRA_BUILD_FLAGS="-DDISABLE_CERN_ROOT=0 -DSTIR_OPENMP:BOOL=ON -DDISABLE_PETSIRD:BOOL=ON" CC=gcc-9 CXX=g++-9 - os: linux dist: focal python: 3 @@ -69,7 +69,7 @@ matrix: packages: - *addons_apt_packages - [g++-10, nlohmann-json3-dev] - env: EXTRA_BUILD_FLAGS="-DDISABLE_CERN_ROOT=1 -DSTIR_OPENMP:BOOL=ON" CC=gcc-10 CXX=g++-10 + env: EXTRA_BUILD_FLAGS="-DDISABLE_CERN_ROOT=1 -DSTIR_OPENMP:BOOL=ON -DDISABLE_PETSIRD:BOOL=ON" CC=gcc-10 CXX=g++-10 - os: linux # note: can't get it to install on focal due to a package problem dist: bionic @@ -79,14 +79,14 @@ matrix: packages: - *addons_apt_packages - [clang, libomp-dev] - env: EXTRA_BUILD_FLAGS="-DDISABLE_CERN_ROOT=1 -DSTIR_OPENMP:BOOL=ON" CC=clang CXX=clang++ + env: EXTRA_BUILD_FLAGS="-DDISABLE_CERN_ROOT=1 -DSTIR_OPENMP:BOOL=ON -DDISABLE_PETSIRD:BOOL=ON" CC=clang CXX=clang++ #### osx # note: cannot use OpenMP on OSX yet, see https://github.com/UCL/STIR/issues/117 - os: osx osx_image: xcode12.2 python: 3 - env: EXTRA_BUILD_FLAGS="-DDISABLE_CERN_ROOT=1 -DSTIR_OPENMP:BOOL=OFF" CC=gcc CXX=g++ + env: EXTRA_BUILD_FLAGS="-DDISABLE_CERN_ROOT=1 -DSTIR_OPENMP:BOOL=OFF -DDISABLE_PETSIRD:BOOL=ON" CC=gcc CXX=g++ # Disable as ROOT is currently failing via brew (as it wants to build it, and which causes a timeout) #- os: osx # python: 3 @@ -94,7 +94,7 @@ matrix: - os: osx osx_image: xcode11.3 python: 3 - env: EXTRA_BUILD_FLAGS="-DDISABLE_CERN_ROOT=1 -DSTIR_OPENMP:BOOL=OFF -DSTIR_ENABLE_EXPERIMENTAL:BOOL=ON" CC=clang CXX=clang++ + env: EXTRA_BUILD_FLAGS="-DDISABLE_CERN_ROOT=1 -DSTIR_OPENMP:BOOL=OFF -DSTIR_ENABLE_EXPERIMENTAL:BOOL=ON -DDISABLE_PETSIRD:BOOL=ON" CC=clang CXX=clang++ # osx, OpenMP using llvm clang # disable due to LLVM clang 12.0.1 problem @@ -105,7 +105,7 @@ matrix: env: global: - - BUILD_FLAGS="-DBUILD_SWIG_PYTHON:BOOL=On -DSTIR_MPI:BOOL=Off -DCMAKE_BUILD_TYPE=Release" + - BUILD_FLAGS="-DBUILD_SWIG_PYTHON:BOOL=On -DSTIR_MPI:BOOL=Off -DDISABLE_PETSIRD:BOOL=ON -DCMAKE_BUILD_TYPE=Release" # No need for sudo sudo: false From cf2d1d97cf1647b42408dbf41974c6d54fe1cabf Mon Sep 17 00:00:00 2001 From: NikEfth Date: Tue, 22 Jul 2025 12:34:00 -0400 Subject: [PATCH 27/42] * Guard for undefined bulk materials * Relax epsilon for block angular spacing calculation --- src/listmode_buildblock/CListModeDataPETSIRD.cxx | 11 ++++++++--- 1 file changed, 8 insertions(+), 3 deletions(-) diff --git a/src/listmode_buildblock/CListModeDataPETSIRD.cxx b/src/listmode_buildblock/CListModeDataPETSIRD.cxx index 995afb38ee..0767530470 100644 --- a/src/listmode_buildblock/CListModeDataPETSIRD.cxx +++ b/src/listmode_buildblock/CListModeDataPETSIRD.cxx @@ -275,8 +275,13 @@ CListModeDataPETSIRD::isCylindricalConfiguration(const petsird::ScannerInformati const std::vector& replicated_module_list) { // Determine DOI based on material - const std::string& material = scanner_info.bulk_materials[0].name; - float average_doi = (material == "BGO") ? 5.0f : (material == "LSO" || material == "LYSO") ? 7.0f : 0.0f; + float average_doi = 0.0; + if (scanner_info.bulk_materials.size() > 0) + { + const std::string& material = scanner_info.bulk_materials[0].name; + if (material.size() > 0) + average_doi = (material == "BGO") ? 5.0f : (material == "LSO" || material == "LYSO") ? 7.0f : 0.0f; + } const petsird::TypeOfModule type_of_module = replicated_module_list.size(); if (type_of_module > 1) @@ -325,7 +330,7 @@ CListModeDataPETSIRD::isCylindricalConfiguration(const petsird::ScannerInformati figure_out_block_angles(unique_angle_modules, rotation_axis, replicated_module_list); std::vector block_angular_spacing; - if (!get_spacing_uniform(block_angular_spacing, unique_angle_modules)) /// epsilon * 10000) // relax epsilon here + if (!get_spacing_uniform(block_angular_spacing, unique_angle_modules, 1e-2)) /// epsilon * 10000) // relax epsilon here return false; std::vector element_horizontal_spacing, element_vertical_spacing; From 2447c30e25a59c5e2ae98ce98205ecf05f860c47 Mon Sep 17 00:00:00 2001 From: NikEfth Date: Tue, 22 Jul 2025 14:57:47 -0400 Subject: [PATCH 28/42] Work on get_next_record and CListEventPETSIRD --- .../stir/listmode/CListModeDataPETSIRD.h | 8 +++- .../stir/listmode/CListRecordPETSIRD.h | 23 +++++----- .../CListModeDataPETSIRD.cxx | 46 +++++++++++++++---- 3 files changed, 55 insertions(+), 22 deletions(-) diff --git a/src/include/stir/listmode/CListModeDataPETSIRD.h b/src/include/stir/listmode/CListModeDataPETSIRD.h index c65f34389e..bfdd7894e4 100644 --- a/src/include/stir/listmode/CListModeDataPETSIRD.h +++ b/src/include/stir/listmode/CListModeDataPETSIRD.h @@ -62,7 +62,7 @@ class CListModeDataPETSIRD : public CListModeDataBasedOnCoordinateMap Succeeded set_get_position(const SavedPosition& pos) override {} - virtual bool has_delayeds() const override { return true; } + virtual bool has_delayeds() const override { return m_has_delayeds; } Succeeded reset() override {} @@ -84,10 +84,14 @@ class CListModeDataPETSIRD : public CListModeDataBasedOnCoordinateMap mutable petsird::EventTimeBlock curr_event_block; - const petsird::TypeOfModulePair type_of_module_pair{ 0, 0 }; + petsird::TypeOfModulePair type_of_module_pair{ 0, 0 }; shared_ptr this_scanner_sptr; + mutable bool curr_is_prompt = true; + + mutable bool m_has_delayeds = false; + bool isCylindricalConfiguration(const petsird::ScannerInformation& scanner_info, const std::vector& replicated_module_list); diff --git a/src/include/stir/listmode/CListRecordPETSIRD.h b/src/include/stir/listmode/CListRecordPETSIRD.h index 867a98ed78..08d7c04bb9 100644 --- a/src/include/stir/listmode/CListRecordPETSIRD.h +++ b/src/include/stir/listmode/CListRecordPETSIRD.h @@ -69,7 +69,7 @@ class CListEventPETSIRD : public CListEvent inline void get_bin(Bin& bin, const ProjDataInfo& proj_data_info) const override; //! Returns 0 if event is prompt and 1 if delayed - inline bool is_prompt() const override { return true; } + inline bool is_prompt() const override { return m_is_prompt; } inline void set_map_sptr(shared_ptr new_map_sptr) { map_sptr = new_map_sptr; } /*! Set the scanner */ @@ -78,10 +78,13 @@ class CListEventPETSIRD : public CListEvent virtual bool is_valid_template(const ProjDataInfo&) const override { return true; } + inline void set_type(bool val) { m_is_prompt = val; }; + private: shared_ptr map_sptr; shared_ptr scanner_sptr; + bool m_is_prompt = true; const DetectorCoordinateMap& map_to_use() const { return map_sptr ? *map_sptr : *this->scanner_sptr->get_detector_map_sptr(); } }; @@ -90,13 +93,14 @@ class CListEventPETSIRD : public CListEvent class CListTimePETSIRD : public ListTime { public: - inline unsigned long get_time_in_millisecs() const { /*return static_cast(time);*/ } + inline unsigned long get_time_in_millisecs() const { return static_cast(time); } inline Succeeded set_time_in_millisecs(const unsigned long time_in_millisecs) { - // time = ((boost::uint64_t(1) << 49) - 1) & static_cast(time_in_millisecs); + time = time_in_millisecs; return Succeeded::yes; } - inline bool is_time() const { /*return type; */ } + inline bool is_time() const { return true; } + uint32_t time; }; class CListRecordPETSIRD : public CListRecord @@ -106,9 +110,9 @@ class CListRecordPETSIRD : public CListRecord ~CListRecordPETSIRD() override {} - bool is_time() const override { /*return time_data.is_time();*/ } + bool is_time() const override { return time_data.is_time(); } - bool is_event() const override { /*return !time_data.is_time();*/ } + bool is_event() const override { return true; } ListEvent& event() override { return event_data; } const ListEvent& event() const override { return event_data; } @@ -127,12 +131,9 @@ class CListRecordPETSIRD : public CListRecord // inline bool is_prompt() const override { /*return event_data.is_prompt();*/ } - Succeeded init_from_data_ptr(const petsird::CoincidenceEvent&) + Succeeded init_from_data_ptr(const petsird::CoincidenceEvent& data, bool is_prompt = true) { - // assert(size_of_record >= 8); - // std::copy(data_ptr, data_ptr + 8, reinterpret_cast(&raw)); // TODO necessary for operator== - // if (do_byte_swap) - // ByteOrder::swap_order(raw); + event_data.set_type(is_prompt); return Succeeded::yes; } diff --git a/src/listmode_buildblock/CListModeDataPETSIRD.cxx b/src/listmode_buildblock/CListModeDataPETSIRD.cxx index 0767530470..1964ac76ae 100644 --- a/src/listmode_buildblock/CListModeDataPETSIRD.cxx +++ b/src/listmode_buildblock/CListModeDataPETSIRD.cxx @@ -284,6 +284,7 @@ CListModeDataPETSIRD::isCylindricalConfiguration(const petsird::ScannerInformati } const petsird::TypeOfModule type_of_module = replicated_module_list.size(); + if (type_of_module > 1) { info("Multiple types of PETSIRD modules are not supported. Abord."); @@ -477,23 +478,50 @@ CListModeDataPETSIRD::get_empty_record_sptr() const Succeeded CListModeDataPETSIRD::get_next_record(CListRecord& record_of_general_type) const { - CListRecordPETSIRD& record = dynamic_cast(record_of_general_type); - petsird::CoincidenceEvent& curr_event - = curr_event_block.prompt_events[type_of_module_pair[0]][type_of_module_pair[1]].at(curr_event_in_event_block); + auto& record = dynamic_cast(record_of_general_type); + + const auto& prompt_list = curr_event_block.prompt_events.at(0).at(0); // TODO: support mulitple pairs of modules. + const auto& delayed_list = m_has_delayeds ? curr_event_block.delayed_events->at(0).at(0) : prompt_list; + + const auto& event_list = curr_is_prompt ? prompt_list : delayed_list; - Succeeded ok = record.init_from_data_ptr(curr_event); + if (record.init_from_data_ptr(event_list.at(curr_event_in_event_block), curr_is_prompt) == Succeeded::no + || record.time().set_time_in_millisecs(curr_event_block.time_interval.start) == Succeeded::no) + { + return Succeeded::no; + } + + ++curr_event_in_event_block; + + if (curr_event_in_event_block < event_list.size()) + { + return Succeeded::yes; + } - if (ok == Succeeded::no) - return Succeeded::no; + // -Once we hit the size of the vector + curr_event_in_event_block = 0; - curr_event_in_event_block++; - if (curr_event_in_event_block == curr_event_block.prompt_events.size()) + if (!m_has_delayeds || curr_is_prompt) { + if (m_has_delayeds) + { + curr_is_prompt = false; + } + else + { + if (!current_lm_data_ptr->ReadTimeBlocks(curr_time_block)) + return Succeeded::no; + curr_event_block = std::get(curr_time_block); + } + } + else + { + curr_is_prompt = true; if (!current_lm_data_ptr->ReadTimeBlocks(curr_time_block)) return Succeeded::no; curr_event_block = std::get(curr_time_block); - curr_event_in_event_block = 0; } + return Succeeded::yes; } From cf496057b8d41a4ac57cd2b31924f9414981f08b Mon Sep 17 00:00:00 2001 From: NikEfth Date: Tue, 22 Jul 2025 18:47:14 -0400 Subject: [PATCH 29/42] Finished the get_next_record method to handle PETSIRD records. --- .../stir/listmode/CListModeDataPETSIRD.h | 18 ++--- .../stir/listmode/CListRecordPETSIRD.h | 76 ++++++++++++------- .../stir/listmode/CListRecordPETSIRD.inl | 19 ++--- .../CListModeDataPETSIRD.cxx | 49 ++++++++---- src/listmode_buildblock/LmToProjData.cxx | 2 +- 5 files changed, 103 insertions(+), 61 deletions(-) diff --git a/src/include/stir/listmode/CListModeDataPETSIRD.h b/src/include/stir/listmode/CListModeDataPETSIRD.h index bfdd7894e4..0783243db4 100644 --- a/src/include/stir/listmode/CListModeDataPETSIRD.h +++ b/src/include/stir/listmode/CListModeDataPETSIRD.h @@ -56,17 +56,15 @@ class CListModeDataPETSIRD : public CListModeDataBasedOnCoordinateMap virtual shared_ptr get_empty_record_sptr() const override; - virtual Succeeded get_next_record(CListRecord& record_of_general_type) const override; + Succeeded get_next_record(CListRecord& record_of_general_type) const override; - SavedPosition save_get_position() override {} + SavedPosition save_get_position() override { return static_cast(curr_event_in_event_block); } - Succeeded set_get_position(const SavedPosition& pos) override {} + Succeeded set_get_position(const SavedPosition& pos) override { return Succeeded::yes; } virtual bool has_delayeds() const override { return m_has_delayeds; } - Succeeded reset() override {} - - inline unsigned long int get_total_number_of_events() const override { return total_events; } + Succeeded reset() override { return Succeeded::yes; } protected: virtual Succeeded open_lm_file() const override; @@ -76,12 +74,14 @@ class CListModeDataPETSIRD : public CListModeDataBasedOnCoordinateMap private: const bool use_hdf5; - unsigned long int total_events = 0; - mutable unsigned long int curr_event_in_event_block = 0; mutable petsird::TimeBlock curr_time_block; + int numberOfModules; + + int numberOfElementsIndices; + mutable petsird::EventTimeBlock curr_event_block; petsird::TypeOfModulePair type_of_module_pair{ 0, 0 }; @@ -90,7 +90,7 @@ class CListModeDataPETSIRD : public CListModeDataBasedOnCoordinateMap mutable bool curr_is_prompt = true; - mutable bool m_has_delayeds = false; + mutable bool m_has_delayeds; bool isCylindricalConfiguration(const petsird::ScannerInformation& scanner_info, const std::vector& replicated_module_list); diff --git a/src/include/stir/listmode/CListRecordPETSIRD.h b/src/include/stir/listmode/CListRecordPETSIRD.h index 08d7c04bb9..1389db2e15 100644 --- a/src/include/stir/listmode/CListRecordPETSIRD.h +++ b/src/include/stir/listmode/CListRecordPETSIRD.h @@ -44,9 +44,9 @@ Coincidence Event Class for PETSIRD: Header File #include "boost/cstdint.hpp" #include "stir/DetectorCoordinateMap.h" -#include "boost/make_shared.hpp" +#include "types.h" -#include "../../PETSIRD/cpp/generated/types.h" +// #include "../../PETSIRD/cpp/generated/types.h" START_NAMESPACE_STIR @@ -68,9 +68,6 @@ class CListEventPETSIRD : public CListEvent //! Override the default implementation inline void get_bin(Bin& bin, const ProjDataInfo& proj_data_info) const override; - //! Returns 0 if event is prompt and 1 if delayed - inline bool is_prompt() const override { return m_is_prompt; } - inline void set_map_sptr(shared_ptr new_map_sptr) { map_sptr = new_map_sptr; } /*! Set the scanner */ /*! Currently only used if the map is not set. */ @@ -78,18 +75,34 @@ class CListEventPETSIRD : public CListEvent virtual bool is_valid_template(const ProjDataInfo&) const override { return true; } - inline void set_type(bool val) { m_is_prompt = val; }; + virtual bool is_prompt() const override { return _prompt; } + + virtual Succeeded set_prompt(const bool prompt) override + { + _prompt = prompt; + return Succeeded::yes; + } + + void set_PETSIRD_ranges(int _numberOfModules, int _numberOfElementsIndices) + { + numberOfModules = _numberOfModules; + numberOfElementsIndices = _numberOfElementsIndices; + } + + int numberOfModules; + + int numberOfElementsIndices; + + std::pair det_0, det_1; private: shared_ptr map_sptr; shared_ptr scanner_sptr; + bool _prompt; - bool m_is_prompt = true; const DetectorCoordinateMap& map_to_use() const { return map_sptr ? *map_sptr : *this->scanner_sptr->get_detector_map_sptr(); } }; -//! Class for record with time data using PETSIRD bitfield definition -/*! \ingroup listmode */ class CListTimePETSIRD : public ListTime { public: @@ -108,32 +121,41 @@ class CListRecordPETSIRD : public CListRecord public: CListRecordPETSIRD() {} - ~CListRecordPETSIRD() override {} + // ~CListRecordPETSIRD() override {} - bool is_time() const override { return time_data.is_time(); } + bool is_time() const override { return true; /*time_data.is_time();*/ } bool is_event() const override { return true; } - ListEvent& event() override { return event_data; } - const ListEvent& event() const override { return event_data; } - - ListTime& time() override { return time_data; } - const ListTime& time() const override { return time_data; } + CListEventPETSIRD& event() override { return event_data; } + const CListEventPETSIRD& event() const override { /*return event_data;*/ } - CListEventPETSIRD& event_PETSIRD() { return event_data; } + CListTimePETSIRD& time() override { return time_data; } + const CListTimePETSIRD& time() const override { return time_data; } - const CListEventPETSIRD& event_PETSIRD() const { return event_data; } - - // virtual bool operator==(const CListRecordPETSIRD& e2) const - // { - // // return dynamic_cast(&e2) != 0 && raw == static_cast(e2).r; - // } - - // inline bool is_prompt() const override { /*return event_data.is_prompt();*/ } + bool operator==(const CListRecordPETSIRD& e2) const + { + // return dynamic_cast(&e2) != 0 && raw == static_cast(e2).r; + } - Succeeded init_from_data_ptr(const petsird::CoincidenceEvent& data, bool is_prompt = true) + virtual Succeeded init_from_data(const petsird::CoincidenceEvent& data, bool is_prompt = true) { - event_data.set_type(is_prompt); + auto decodeElementAndModuleIndex + = [](int linearIndex, int energyIndex, int numberOfElementsIndices, int numberOfModules) -> std::pair { + int reduced = (linearIndex - energyIndex) / numberOfModules; + int moduleIndex = reduced / numberOfElementsIndices; + int elementIndex = reduced % numberOfElementsIndices; + return { elementIndex, moduleIndex }; + }; + + event_data.det_0 + = decodeElementAndModuleIndex(data.detection_bins[0], 0, event_data.numberOfElementsIndices, event_data.numberOfModules); + event_data.det_1 + = decodeElementAndModuleIndex(data.detection_bins[1], 0, event_data.numberOfElementsIndices, event_data.numberOfModules); + + std::cout << event_data.det_0.first << " " << event_data.det_0.second << std::endl; + std::cout << event_data.det_1.first << " " << event_data.det_1.second << std::endl; + event_data.set_prompt(is_prompt); return Succeeded::yes; } diff --git a/src/include/stir/listmode/CListRecordPETSIRD.inl b/src/include/stir/listmode/CListRecordPETSIRD.inl index 933cf7a944..093297db10 100644 --- a/src/include/stir/listmode/CListRecordPETSIRD.inl +++ b/src/include/stir/listmode/CListRecordPETSIRD.inl @@ -34,8 +34,6 @@ \author Daniel Deidda */ -#include - #include "stir/LORCoordinates.h" #include "stir/listmode/CListRecord.h" #include "stir/ProjDataInfo.h" @@ -54,19 +52,22 @@ START_NAMESPACE_STIR LORAs2Points CListEventPETSIRD::get_LOR() const { - // LORAs2Points lor; - // DetectionPositionPair<> det_pos_pair; + LORAs2Points lor; + DetectionPositionPair<> det_pos_pair; - // // static_cast(this)->get_data().get_detection_position_pair(det_pos_pair); + // this->get_data().get_detection_position_pair(det_pos_pair); - // lor.p1() = map_to_use().get_coordinate_for_index(det_pos_pair.pos1()); - // lor.p2() = map_to_use().get_coordinate_for_index(det_pos_pair.pos2()); + lor.p1() = map_to_use().get_coordinate_for_index(det_pos_pair.pos1()); + lor.p2() = map_to_use().get_coordinate_for_index(det_pos_pair.pos2()); - // return lor; + return lor; } void CListEventPETSIRD::get_bin(Bin& bin, const ProjDataInfo& proj_data_info) const -{} +{ + + int nikos = 0; +} END_NAMESPACE_STIR diff --git a/src/listmode_buildblock/CListModeDataPETSIRD.cxx b/src/listmode_buildblock/CListModeDataPETSIRD.cxx index 1964ac76ae..edbd16c27b 100644 --- a/src/listmode_buildblock/CListModeDataPETSIRD.cxx +++ b/src/listmode_buildblock/CListModeDataPETSIRD.cxx @@ -297,7 +297,8 @@ CListModeDataPETSIRD::isCylindricalConfiguration(const petsird::ScannerInformati std::set unique_tof_values; find_uniqe_values_2D(unique_tof_values, scanner_info.tof_resolution); - int num_modules = replicated_module_list[0].transforms.size(); + numberOfModules = replicated_module_list[0].NumberOfObjects(); + numberOfElementsIndices = replicated_module_list[0].object.detecting_elements.NumberOfObjects(); std::set unique_dim1_values, unique_dim2_values, unique_dim3_values; std::set unique_tof_resolutions; @@ -313,7 +314,7 @@ CListModeDataPETSIRD::isCylindricalConfiguration(const petsird::ScannerInformati info(format("I counted {} axial blocks with spacing {}", unique_dim3_values.size(), block_axial_spacing[0])); - int num_transaxial_blocks = num_modules / main_axis.size(); + int num_transaxial_blocks = numberOfModules / main_axis.size(); info(format("I deduce that the scanner has {} transaxial number of blocks", num_transaxial_blocks)); float radius = 0; @@ -409,7 +410,7 @@ CListModeDataPETSIRD::isCylindricalConfiguration(const petsird::ScannerInformati CListModeDataPETSIRD::CListModeDataPETSIRD(const std::string& listmode_filename, bool use_hdf5) : use_hdf5(use_hdf5) { - this->listmode_filename = listmode_filename; + CListModeDataBasedOnCoordinateMap::listmode_filename = listmode_filename; petsird::Header header; if (use_hdf5) @@ -436,7 +437,7 @@ CListModeDataPETSIRD::CListModeDataPETSIRD(const std::string& listmode_filename, if (isCylindricalConfiguration(scanner_info, replicated_module_list)) { int tof_mash_factor = 1; - proj_data_info_sptr = std::const_pointer_cast( + this->set_proj_data_info_sptr(std::const_pointer_cast( ProjDataInfo::construct_proj_data_info(this_scanner_sptr, 1, this_scanner_sptr->get_num_rings() - 1, @@ -444,23 +445,37 @@ CListModeDataPETSIRD::CListModeDataPETSIRD(const std::string& listmode_filename, this_scanner_sptr->get_max_num_non_arccorrected_bins(), /* arc_correction*/ false, tof_mash_factor) - ->create_shared_clone()); + ->create_shared_clone())); } else { error("TODO:GenericScanner"); } - // if (this->open_lm_file() == Succeeded::no) - // { - // error("CListModeDataPETSIRD: Could not open listmode file " + listmode_filename + "\n"); - // } + shared_ptr _exam_info_sptr(new ExamInfo); + // Only PET scanners supported + _exam_info_sptr->imaging_modality = ImagingModality::PT; + _exam_info_sptr->originating_system = std::string("PETSIRD_defined_scanner"); + // _exam_info_sptr->set_low_energy_thres(scanner_i); + // _exam_info_sptr->set_high_energy_thres(this->root_file_sptr->get_up_energy_thres()); + + this->exam_info_sptr = _exam_info_sptr; + + // N.E.: In my experience the first time block is always empty. + // So I use this unncessessary call to skip to the next. + if (this->open_lm_file() == Succeeded::no) + { + error("CListModeDataPETSIRD: Could not open listmode file " + listmode_filename + "\n"); + } } Succeeded CListModeDataPETSIRD::open_lm_file() const { // current_lm_data_ptr.reset(new petsird::hdf5::PETSIRDReader(listmode_filename)); + if (!current_lm_data_ptr->ReadTimeBlocks(curr_time_block)) + return Succeeded::no; + curr_event_block = std::get(curr_time_block); return Succeeded::yes; } @@ -468,9 +483,10 @@ shared_ptr CListModeDataPETSIRD::get_empty_record_sptr() const { shared_ptr sptr(new CListRecordPETSIRD); - std::dynamic_pointer_cast(sptr)->event_PETSIRD().set_scanner_sptr( + std::dynamic_pointer_cast(sptr)->event().set_scanner_sptr( this->get_proj_data_info_sptr()->get_scanner_sptr()); - std::dynamic_pointer_cast(sptr)->event_PETSIRD().set_map_sptr(map); + std::dynamic_pointer_cast(sptr)->event().set_PETSIRD_ranges(numberOfModules, numberOfElementsIndices); + // std::dynamic_pointer_cast(sptr)->event_PETSIRD().set_map_sptr(map); return sptr; } @@ -478,15 +494,18 @@ CListModeDataPETSIRD::get_empty_record_sptr() const Succeeded CListModeDataPETSIRD::get_next_record(CListRecord& record_of_general_type) const { - auto& record = dynamic_cast(record_of_general_type); + auto& record = dynamic_cast(record_of_general_type); const auto& prompt_list = curr_event_block.prompt_events.at(0).at(0); // TODO: support mulitple pairs of modules. const auto& delayed_list = m_has_delayeds ? curr_event_block.delayed_events->at(0).at(0) : prompt_list; const auto& event_list = curr_is_prompt ? prompt_list : delayed_list; - if (record.init_from_data_ptr(event_list.at(curr_event_in_event_block), curr_is_prompt) == Succeeded::no - || record.time().set_time_in_millisecs(curr_event_block.time_interval.start) == Succeeded::no) + if (event_list.size() == 0) + return Succeeded::no; + + if (record.init_from_data(event_list.at(curr_event_in_event_block), curr_is_prompt) == Succeeded::no + || record_of_general_type.time().set_time_in_millisecs(curr_event_block.time_interval.start) == Succeeded::no) { return Succeeded::no; } @@ -498,7 +517,7 @@ CListModeDataPETSIRD::get_next_record(CListRecord& record_of_general_type) const return Succeeded::yes; } - // -Once we hit the size of the vector + // - Once we hit the size of the vector curr_event_in_event_block = 0; if (!m_has_delayeds || curr_is_prompt) diff --git a/src/listmode_buildblock/LmToProjData.cxx b/src/listmode_buildblock/LmToProjData.cxx index 87e62b8ad5..37490f3699 100644 --- a/src/listmode_buildblock/LmToProjData.cxx +++ b/src/listmode_buildblock/LmToProjData.cxx @@ -38,7 +38,7 @@ USE_SegmentByView // (Note: can currently NOT be disabled) #define USE_SegmentByView -//#define FRAME_BASED_DT_CORR +// #define FRAME_BASED_DT_CORR // 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. From 3504c099c2854f37953b8db6065933fc2378d463 Mon Sep 17 00:00:00 2001 From: danieldeidda Date: Tue, 12 Aug 2025 09:33:17 +0100 Subject: [PATCH 30/42] update function for spacing estimation to support single module scanners; created map-creation draft, it probably needs some fixing also the call to set_map_fails with a criptic seg fault --- .../CListModeDataPETSIRD.cxx | 70 ++++++++++++++++--- src/listmode_buildblock/CMakeLists.txt | 1 + 2 files changed, 63 insertions(+), 8 deletions(-) diff --git a/src/listmode_buildblock/CListModeDataPETSIRD.cxx b/src/listmode_buildblock/CListModeDataPETSIRD.cxx index edbd16c27b..e72b33928e 100644 --- a/src/listmode_buildblock/CListModeDataPETSIRD.cxx +++ b/src/listmode_buildblock/CListModeDataPETSIRD.cxx @@ -309,11 +309,6 @@ CListModeDataPETSIRD::isCylindricalConfiguration(const petsird::ScannerInformati const std::set& main_axis = getLargestVector(unique_dim1_values, unique_dim2_values, unique_dim3_values); - std::vector block_axial_spacing; - get_spacing_uniform(block_axial_spacing, main_axis); - - info(format("I counted {} axial blocks with spacing {}", unique_dim3_values.size(), block_axial_spacing[0])); - int num_transaxial_blocks = numberOfModules / main_axis.size(); info(format("I deduce that the scanner has {} transaxial number of blocks", num_transaxial_blocks)); @@ -351,6 +346,20 @@ CListModeDataPETSIRD::isCylindricalConfiguration(const petsird::ScannerInformati error("TODO!"); } + std::vector block_axial_spacing; + get_spacing_uniform(block_axial_spacing, main_axis); + if (block_axial_spacing.size() < 1) + { + // std::set::iterator it = unique_elements_horizontal_values.begin(); + // std::advance(it,0); + float begin = *std::next(unique_elements_horizontal_values.begin(), 0); + // std::advance(it,unique_elements_horizontal_values.size()-1); + float end = *std::next(unique_elements_horizontal_values.begin(), unique_elements_horizontal_values.size() - 1); + block_axial_spacing.push_back(std::abs(end - begin)); + } + + info(format("I counted {} axial blocks with spacing {}", unique_dim3_values.size(), block_axial_spacing[0])); + this_scanner_sptr.reset( new Scanner(Scanner::User_defined_scanner, std::string("PETSIRD_defined_scanner"), @@ -433,8 +442,9 @@ CListModeDataPETSIRD::CListModeDataPETSIRD(const std::string& listmode_filename, curr_event_block = std::get(curr_time_block); else error("CListModeDataPETSIRD: holds_alternative not true. Abord."); - - if (isCylindricalConfiguration(scanner_info, replicated_module_list)) + isCylindricalConfiguration(scanner_info, replicated_module_list); + bool b = false; + if (b) // isCylindricalConfiguration(scanner_info, replicated_module_list)) { int tof_mash_factor = 1; this->set_proj_data_info_sptr(std::const_pointer_cast( @@ -449,7 +459,51 @@ CListModeDataPETSIRD::CListModeDataPETSIRD(const std::string& listmode_filename, } else { - error("TODO:GenericScanner"); + // this_scanner_sptr.get_ + DetectorCoordinateMap::det_pos_to_coord_type petsird_map; + const petsird::TypeOfModule type_of_module = replicated_module_list.size() - 1; + // const auto event_energy_bin_edges=scanner_info.event_energy_bin_edges[type_of_module]; + // const auto num_event_energy_bins = event_energy_bin_edges.NumberOfBins(); + // error("TODO:GenericScanner"); + for (uint32_t module = 0; module < numberOfModules; module++) + for (uint32_t elem = 0; elem < numberOfElementsIndices; elem++) + // for (uint32_t ener = 0; ener < num_event_energy_bins; ener++) //energy not supported yet + { + int index = module * numberOfElementsIndices + elem; + // Here we are going to assume that the index = module*numberOfElementsIndices+elem is equal to ax+ + // num_ax*tang+ num_ax*num_tang*rad then we will pass this to the map sorter (set_detector_map)) + // therefore we can get ax, tang and rad from index + /* + * rad = index/(num_ax*num_tang) + * tang =(index-rad*num_ax*num_tang)/num_ax -num_tang/2 + * ax = index mod num_ax + */ + + int rad_pos = index / (this_scanner_sptr->get_num_rings() * this_scanner_sptr->get_num_detectors_per_ring()); + int ax_pos = (index - rad_pos * this_scanner_sptr->get_num_rings() * this_scanner_sptr->get_num_detectors_per_ring()) + / this_scanner_sptr->get_num_detectors_per_ring(); + int tang_pos = index % this_scanner_sptr->get_num_detectors_per_ring(); + + DetectionPosition<> detpos(tang_pos, ax_pos, rad_pos); + petsird::ExpandedDetectionBin expanded_detection_bin{ module, elem, 1 }; + + auto box_shape = petsird_helpers::geometry::get_detecting_box(scanner_info, type_of_module, expanded_detection_bin); + CartesianCoordinate3D mean_coord; + + for (auto& corner : box_shape.corners) + { // if STIR (z,y,x) -> PETSIRD (-y, -x, z) pheraps the order below needs to be changed + mean_coord.x() = +corner.c[0] / box_shape.corners.size(); + mean_coord.y() = +corner.c[1] / box_shape.corners.size(); + mean_coord.z() = +corner.c[2] / box_shape.corners.size(); + } + + // save mean pos into map + petsird_map[detpos] = mean_coord; + std::cout << detpos.radial_coord() << "," << detpos.axial_coord() << "," << detpos.tangential_coord() << "," + << mean_coord.x() << "," << mean_coord.y() << "," << mean_coord.z() << "," << std::endl; + } + + this->map->set_detector_map(petsird_map); } shared_ptr _exam_info_sptr(new ExamInfo); diff --git a/src/listmode_buildblock/CMakeLists.txt b/src/listmode_buildblock/CMakeLists.txt index bd65518987..0942a86a15 100644 --- a/src/listmode_buildblock/CMakeLists.txt +++ b/src/listmode_buildblock/CMakeLists.txt @@ -78,6 +78,7 @@ if (HAVE_PETSIRD) target_include_directories(listmode_buildblock PUBLIC $ $ + $ $ ) From c0392cb194b8bcd96890843cb9f8f4017fb45c8f Mon Sep 17 00:00:00 2001 From: Nikos Efthimiou Date: Sun, 9 Nov 2025 15:19:19 +0900 Subject: [PATCH 31/42] Fixes on the initialization --- src/listmode_buildblock/CListModeDataPETSIRD.cxx | 14 +++++++++++++- 1 file changed, 13 insertions(+), 1 deletion(-) diff --git a/src/listmode_buildblock/CListModeDataPETSIRD.cxx b/src/listmode_buildblock/CListModeDataPETSIRD.cxx index e72b33928e..3457bf39e9 100644 --- a/src/listmode_buildblock/CListModeDataPETSIRD.cxx +++ b/src/listmode_buildblock/CListModeDataPETSIRD.cxx @@ -503,7 +503,19 @@ CListModeDataPETSIRD::CListModeDataPETSIRD(const std::string& listmode_filename, << mean_coord.x() << "," << mean_coord.y() << "," << mean_coord.z() << "," << std::endl; } - this->map->set_detector_map(petsird_map); + this->map.reset(new DetectorCoordinateMap(petsird_map)); + + int tof_mash_factor = 1; + this->set_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())); + } shared_ptr _exam_info_sptr(new ExamInfo); From c47ad026b15a21caa0ebe84844620a6fb3eb3d2c Mon Sep 17 00:00:00 2001 From: Nikos Efthimiou Date: Sun, 9 Nov 2025 16:26:58 +0900 Subject: [PATCH 32/42] update PETSIRD to v0.8.0 --- CMakeLists.txt | 36 +++++++++++++------------- PETSIRD | 2 +- src/IO/CMakeLists.txt | 12 ++++----- src/listmode_buildblock/CMakeLists.txt | 14 +++++----- 4 files changed, 32 insertions(+), 32 deletions(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index 3021aeb9b8..fa9ad268f0 100755 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -106,19 +106,19 @@ option(DISABLE_UPENN "disable use of UPENN filetypes" ON) option(DISABLE_PETSIRD "disable use of PETSIRD filetypes" OFF) find_package(Git QUIET) -if(GIT_FOUND AND EXISTS "${PROJECT_SOURCE_DIR}/.git") -# Update submodules as needed - option(GIT_SUBMODULE "Check submodules during build" ON) - if(GIT_SUBMODULE) - message(STATUS "Submodule update") - execute_process(COMMAND ${GIT_EXECUTABLE} submodule update --init --recursive - WORKING_DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR} - RESULT_VARIABLE GIT_SUBMOD_RESULT) - if(NOT GIT_SUBMOD_RESULT EQUAL "0") - message(FATAL_ERROR "git submodule update --init --recursive failed with ${GIT_SUBMOD_RESULT}, please checkout submodules") - endif() - endif() -endif() +# if(GIT_FOUND AND EXISTS "${PROJECT_SOURCE_DIR}/.git") +# # Update submodules as needed +# option(GIT_SUBMODULE "Check submodules during build" ON) +# if(GIT_SUBMODULE) +# message(STATUS "Submodule update") +# execute_process(COMMAND ${GIT_EXECUTABLE} submodule update --init --recursive +# WORKING_DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR} +# RESULT_VARIABLE GIT_SUBMOD_RESULT) +# if(NOT GIT_SUBMOD_RESULT EQUAL "0") +# message(FATAL_ERROR "git submodule update --init --recursive failed with ${GIT_SUBMOD_RESULT}, please checkout submodules") +# endif() +# endif() +# endif() if(NOT EXISTS "${PROJECT_SOURCE_DIR}/external_helpers/fmt/CMakeLists.txt") message(FATAL_ERROR "The {fmt} submodule was not downloaded! GIT_SUBMODULE was turned off or failed. Please update submodules and try again.") @@ -305,11 +305,11 @@ if(NOT DISABLE_PETSIRD) set(HAVE_PETSIRD FALSE) message(WARNING "PETSIRD generation failed with code ${JUST_RESULT}. HAVE_PETSIRD not set.") endif() - set(PETSIRD_dir ${PROJECT_SOURCE_DIR}/PETSIRD/cpp/generated) - add_subdirectory(${PETSIRD_dir} PETSIRD_generated) - install(TARGETS petsird_generated - EXPORT STIRTargets - DESTINATION lib) + set(PETSIRD_dir ${PROJECT_SOURCE_DIR}/PETSIRD/cpp) + add_subdirectory(${PETSIRD_dir} PETSIRD) + #install(TARGETS petsird_generated + # EXPORT STIRTargets + # DESTINATION lib) endif() #### enable support for ctest diff --git a/PETSIRD b/PETSIRD index 96c1dc0120..fa2f04321c 160000 --- a/PETSIRD +++ b/PETSIRD @@ -1 +1 @@ -Subproject commit 96c1dc0120fea2900b504fad1ab241aa2b1bd7d9 +Subproject commit fa2f04321cba17233817062d64251ed4df72caf9 diff --git a/src/IO/CMakeLists.txt b/src/IO/CMakeLists.txt index 0b88f5b43f..94afd69f69 100644 --- a/src/IO/CMakeLists.txt +++ b/src/IO/CMakeLists.txt @@ -148,11 +148,11 @@ if (HAVE_PETSIRD) # #set(PETSIRD_dir ../../PETSIRD/cpp/generated) -target_include_directories(IO PUBLIC - $ - $ - $ -) +#target_include_directories(IO PUBLIC +# $ +# $ +# $ +#) # # # target_include_directories(IO PUBLIC @@ -184,7 +184,7 @@ target_include_directories(IO PUBLIC # # needed for helpers # target_include_directories(IO PUBLIC ${PETSIRD_dir}/..) # target_include_directories(IO PUBLIC ${PETSIRD_dir}/../helpers/include) -target_link_libraries(IO PUBLIC petsird_generated) +target_link_libraries(IO PUBLIC petsird_helpers) endif() diff --git a/src/listmode_buildblock/CMakeLists.txt b/src/listmode_buildblock/CMakeLists.txt index 0942a86a15..41853b14c0 100644 --- a/src/listmode_buildblock/CMakeLists.txt +++ b/src/listmode_buildblock/CMakeLists.txt @@ -75,12 +75,12 @@ if (HAVE_PETSIRD) #set(PETSIRD_dir ../../PETSIRD/cpp/generated) #add_subdirectory(${PETSIRD_dir} PETSIRD_generated) -target_include_directories(listmode_buildblock PUBLIC - $ - $ - $ - $ -) +#target_include_directories(listmode_buildblock PUBLIC +# $ +# $ +# $ +# $ +#) # target_include_directories(listmode_buildblock PUBLIC # $ @@ -97,5 +97,5 @@ target_include_directories(listmode_buildblock PUBLIC # $ # ) -target_link_libraries(listmode_buildblock PUBLIC petsird_generated) +target_link_libraries(listmode_buildblock PUBLIC petsird_helpers) endif() From 2517761af6ec3cec43fea418fcba519f2f8af720 Mon Sep 17 00:00:00 2001 From: Nikos Efthimiou Date: Mon, 10 Nov 2025 10:38:58 +0900 Subject: [PATCH 33/42] Fixes in the CMakeLists and PETSIRD listmode files to accommodate changes in the petsird_helpers functions and ScannerInformation structure. --- src/CMakeLists.txt | 2 + src/IO/PETSIRDCListmodeInputFileFormat.cxx | 4 +- .../stir/listmode/CListModeDataPETSIRD.h | 7 ++- .../stir/listmode/CListRecordPETSIRD.h | 56 ++++++++++++------- .../stir/listmode/CListRecordPETSIRD.inl | 19 +++++-- .../CListModeDataPETSIRD.cxx | 35 ++++++------ 6 files changed, 75 insertions(+), 48 deletions(-) diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt index 0020d27d12..f42b1a64d8 100644 --- a/src/CMakeLists.txt +++ b/src/CMakeLists.txt @@ -254,6 +254,8 @@ endif() # Warning: dependencies for object libraries are not transitive! add_library(stir_registries OBJECT ${STIR_REGISTRIES}) target_link_libraries(stir_registries PRIVATE fmt) +target_link_libraries(stir_registries PRIVATE petsird) + # TODO, really should use stir_libs.cmake target_include_directories(stir_registries PRIVATE ${STIR_INCLUDE_DIR}) target_include_directories(stir_registries PRIVATE ${Boost_INCLUDE_DIR}) diff --git a/src/IO/PETSIRDCListmodeInputFileFormat.cxx b/src/IO/PETSIRDCListmodeInputFileFormat.cxx index 4352cc9e20..e941420dc2 100644 --- a/src/IO/PETSIRDCListmodeInputFileFormat.cxx +++ b/src/IO/PETSIRDCListmodeInputFileFormat.cxx @@ -1,6 +1,6 @@ #include "stir/IO/PETSIRDCListmodeInputFileFormat.h" -#include "../../PETSIRD/cpp/generated/binary/protocols.h" -#include "../../PETSIRD/cpp/generated/hdf5/protocols.h" +#include "petsird/binary/protocols.h" +#include "petsird/hdf5/protocols.h" // #include "../../PETSIRD/cpp/generated/types.h" // #include "../../PETSIRD/cpp/helpers/include/petsird_helpers.h" diff --git a/src/include/stir/listmode/CListModeDataPETSIRD.h b/src/include/stir/listmode/CListModeDataPETSIRD.h index 0783243db4..d338eb21c8 100644 --- a/src/include/stir/listmode/CListModeDataPETSIRD.h +++ b/src/include/stir/listmode/CListModeDataPETSIRD.h @@ -37,7 +37,7 @@ Coincidence LM Data Class for PETSIRD #include "stir/listmode/CListRecord.h" #include "stir/shared_ptr.h" -#include "../../PETSIRD/cpp/generated/protocols.h" +#include "petsird/protocols.h" START_NAMESPACE_STIR @@ -88,12 +88,13 @@ class CListModeDataPETSIRD : public CListModeDataBasedOnCoordinateMap shared_ptr this_scanner_sptr; + shared_ptr scanner_info; + mutable bool curr_is_prompt = true; mutable bool m_has_delayeds; - bool isCylindricalConfiguration(const petsird::ScannerInformation& scanner_info, - const std::vector& replicated_module_list); + bool isCylindricalConfiguration(const std::vector& replicated_module_list); void find_uniqe_values_1D(std::set& values, const std::vector& input); diff --git a/src/include/stir/listmode/CListRecordPETSIRD.h b/src/include/stir/listmode/CListRecordPETSIRD.h index 1389db2e15..85e722dc22 100644 --- a/src/include/stir/listmode/CListRecordPETSIRD.h +++ b/src/include/stir/listmode/CListRecordPETSIRD.h @@ -40,11 +40,11 @@ Coincidence Event Class for PETSIRD: Header File #include "stir/DetectionPositionPair.h" #include "stir/Succeeded.h" #include "stir/ByteOrderDefine.h" - +#include "petsird_helpers.h" #include "boost/cstdint.hpp" #include "stir/DetectorCoordinateMap.h" -#include "types.h" +//#include "petsird/types.h" // #include "../../PETSIRD/cpp/generated/types.h" @@ -66,7 +66,7 @@ class CListEventPETSIRD : public CListEvent inline LORAs2Points get_LOR() const override; //! Override the default implementation - inline void get_bin(Bin& bin, const ProjDataInfo& proj_data_info) const override; + // inline void get_bin(Bin& bin, const ProjDataInfo& proj_data_info) const override; inline void set_map_sptr(shared_ptr new_map_sptr) { map_sptr = new_map_sptr; } /*! Set the scanner */ @@ -119,7 +119,8 @@ class CListTimePETSIRD : public ListTime class CListRecordPETSIRD : public CListRecord { public: - CListRecordPETSIRD() {} + CListRecordPETSIRD(shared_ptr scanner_info) + : scanner_info(scanner_info) {} // ~CListRecordPETSIRD() override {} @@ -140,21 +141,36 @@ class CListRecordPETSIRD : public CListRecord virtual Succeeded init_from_data(const petsird::CoincidenceEvent& data, bool is_prompt = true) { - auto decodeElementAndModuleIndex - = [](int linearIndex, int energyIndex, int numberOfElementsIndices, int numberOfModules) -> std::pair { - int reduced = (linearIndex - energyIndex) / numberOfModules; - int moduleIndex = reduced / numberOfElementsIndices; - int elementIndex = reduced % numberOfElementsIndices; - return { elementIndex, moduleIndex }; - }; - - event_data.det_0 - = decodeElementAndModuleIndex(data.detection_bins[0], 0, event_data.numberOfElementsIndices, event_data.numberOfModules); - event_data.det_1 - = decodeElementAndModuleIndex(data.detection_bins[1], 0, event_data.numberOfElementsIndices, event_data.numberOfModules); - - std::cout << event_data.det_0.first << " " << event_data.det_0.second << std::endl; - std::cout << event_data.det_1.first << " " << event_data.det_1.second << std::endl; + // auto decodeElementAndModuleIndex + // = [](int linearIndex, int energyIndex, int numberOfElementsIndices, int numberOfModules) -> std::pair { + // int reduced = (linearIndex - energyIndex) / numberOfModules; + // int moduleIndex = reduced / numberOfElementsIndices; + // int elementIndex = reduced % numberOfElementsIndices; + // return { elementIndex, moduleIndex }; + // }; + + // help expand detection + //const ScannerInformation& scanner, const TypeOfModule& type_of_module, const T& list_of_detection_bins + auto dd = petsird_helpers::expand_detection_bin(*scanner_info, + 0, + data.detection_bins[0]); + event_data.det_0 = { dd.element_index, dd.module_index }; + std::cout << "Expanded 1: " << dd.module_index << " " << dd.element_index << std::endl; + auto ee = petsird_helpers::expand_detection_bin(*scanner_info, + 0, + data.detection_bins[1]); + event_data.det_1 = { ee.element_index, ee.module_index }; + std::cout << "Expanded 2: " << ee.module_index << " " << ee.element_index << std::endl; + + //(data.detection_bins, event_data.numberOfElementsIndices, event_data.numberOfModules); + + // event_data.det_0 + // = decodeElementAndModuleIndex(data.detection_bins[0], 0, event_data.numberOfElementsIndices, event_data.numberOfModules); + // event_data.det_1 + // = decodeElementAndModuleIndex(data.detection_bins[1], 0, event_data.numberOfElementsIndices, event_data.numberOfModules); + + // std::cout << event_data.det_0.first << " " << event_data.det_0.second << std::endl; + // std::cout << event_data.det_1.first << " " << event_data.det_1.second << std::endl; event_data.set_prompt(is_prompt); return Succeeded::yes; } @@ -162,6 +178,8 @@ class CListRecordPETSIRD : public CListRecord private: CListEventPETSIRD event_data; CListTimePETSIRD time_data; + + shared_ptr scanner_info;// = header.scanner; }; END_NAMESPACE_STIR diff --git a/src/include/stir/listmode/CListRecordPETSIRD.inl b/src/include/stir/listmode/CListRecordPETSIRD.inl index 093297db10..169c7aef50 100644 --- a/src/include/stir/listmode/CListRecordPETSIRD.inl +++ b/src/include/stir/listmode/CListRecordPETSIRD.inl @@ -56,18 +56,25 @@ CListEventPETSIRD::get_LOR() const DetectionPositionPair<> det_pos_pair; // this->get_data().get_detection_position_pair(det_pos_pair); - + std::cout << det_pos_pair.pos1().axial_coord() << " " << det_pos_pair.pos1().radial_coord() << " " << det_pos_pair.pos1().tangential_coord() << std::endl; + std::cout << det_pos_pair.pos2().axial_coord() << " " << det_pos_pair.pos2().radial_coord() << " " << det_pos_pair.pos2().tangential_coord() << std::endl; lor.p1() = map_to_use().get_coordinate_for_index(det_pos_pair.pos1()); lor.p2() = map_to_use().get_coordinate_for_index(det_pos_pair.pos2()); + std::cout << "lor_p1" << lor.p1().x() << " " << lor.p1().y() << " " << lor.p1().z() << std::endl; + std::cout << "lor_p2" < det_pos_pair; +// this->get_data().get_detection_position_pair(det_pos_pair); + + +// int nikos = 0; +// } END_NAMESPACE_STIR diff --git a/src/listmode_buildblock/CListModeDataPETSIRD.cxx b/src/listmode_buildblock/CListModeDataPETSIRD.cxx index 3457bf39e9..03222bedee 100644 --- a/src/listmode_buildblock/CListModeDataPETSIRD.cxx +++ b/src/listmode_buildblock/CListModeDataPETSIRD.cxx @@ -30,12 +30,12 @@ Coincidence LM Data Class for PETSIRD: Implementation #include "stir/info.h" #include "stir/error.h" -#include "../../PETSIRD/cpp/helpers/include/petsird_helpers.h" -#include "helpers/include/petsird_helpers/create.h" -#include "helpers/include/petsird_helpers/geometry.h" +#include "petsird_helpers.h" +#include "petsird_helpers/create.h" +#include "petsird_helpers/geometry.h" -#include "../../PETSIRD/cpp/generated/binary/protocols.h" -#include "../../PETSIRD/cpp/generated/hdf5/protocols.h" +#include "petsird/binary/protocols.h" +#include "petsird/hdf5/protocols.h" #include "stir/listmode/CListModeDataPETSIRD.h" #include "stir/listmode/CListRecordPETSIRD.h" @@ -271,14 +271,13 @@ CListModeDataPETSIRD::figure_out_block_angles(std::set& unique_angle_modu } bool -CListModeDataPETSIRD::isCylindricalConfiguration(const petsird::ScannerInformation& scanner_info, - const std::vector& replicated_module_list) +CListModeDataPETSIRD::isCylindricalConfiguration(const std::vector& replicated_module_list) { // Determine DOI based on material float average_doi = 0.0; - if (scanner_info.bulk_materials.size() > 0) + if (scanner_info->bulk_materials.size() > 0) { - const std::string& material = scanner_info.bulk_materials[0].name; + const std::string& material = scanner_info->bulk_materials[0].name; if (material.size() > 0) average_doi = (material == "BGO") ? 5.0f : (material == "LSO" || material == "LYSO") ? 7.0f : 0.0f; } @@ -291,11 +290,11 @@ CListModeDataPETSIRD::isCylindricalConfiguration(const petsird::ScannerInformati return false; } - const auto& tof_bin_edges = scanner_info.tof_bin_edges[type_of_module - 1][type_of_module - 1]; + const auto& tof_bin_edges = scanner_info->tof_bin_edges[type_of_module - 1][type_of_module - 1]; std::cout << "Num. of TOF bins " << tof_bin_edges.NumberOfBins() << std::endl; std::set unique_tof_values; - find_uniqe_values_2D(unique_tof_values, scanner_info.tof_resolution); + find_uniqe_values_2D(unique_tof_values, scanner_info->tof_resolution); numberOfModules = replicated_module_list[0].NumberOfObjects(); numberOfElementsIndices = replicated_module_list[0].object.detecting_elements.NumberOfObjects(); @@ -393,7 +392,7 @@ CListModeDataPETSIRD::isCylindricalConfiguration(const petsird::ScannerInformati unique_elements_vertical_values.size(), /*num_detector_layers_v*/ 1, // num_detector_layers_v - scanner_info.energy_resolution_at_511.front(), // energy_resolution_v + scanner_info->energy_resolution_at_511.front(), // energy_resolution_v 511, // reference_energy_v 1, 0.F, @@ -428,8 +427,8 @@ CListModeDataPETSIRD::CListModeDataPETSIRD(const std::string& listmode_filename, current_lm_data_ptr.reset(new petsird::binary::PETSIRDReader(listmode_filename)); current_lm_data_ptr->ReadHeader(header); - petsird::ScannerInformation scanner_info = header.scanner; - petsird::ScannerGeometry scanner_geo = scanner_info.scanner_geometry; + scanner_info = std::make_shared(header.scanner); + petsird::ScannerGeometry scanner_geo = scanner_info->scanner_geometry; std::vector replicated_module_list = scanner_geo.replicated_modules; // Get the first TimeBlock @@ -442,7 +441,7 @@ CListModeDataPETSIRD::CListModeDataPETSIRD(const std::string& listmode_filename, curr_event_block = std::get(curr_time_block); else error("CListModeDataPETSIRD: holds_alternative not true. Abord."); - isCylindricalConfiguration(scanner_info, replicated_module_list); + isCylindricalConfiguration(replicated_module_list); bool b = false; if (b) // isCylindricalConfiguration(scanner_info, replicated_module_list)) { @@ -487,7 +486,7 @@ CListModeDataPETSIRD::CListModeDataPETSIRD(const std::string& listmode_filename, DetectionPosition<> detpos(tang_pos, ax_pos, rad_pos); petsird::ExpandedDetectionBin expanded_detection_bin{ module, elem, 1 }; - auto box_shape = petsird_helpers::geometry::get_detecting_box(scanner_info, type_of_module, expanded_detection_bin); + auto box_shape = petsird_helpers::geometry::get_detecting_box(*scanner_info, type_of_module, expanded_detection_bin); CartesianCoordinate3D mean_coord; for (auto& corner : box_shape.corners) @@ -548,7 +547,7 @@ CListModeDataPETSIRD::open_lm_file() const shared_ptr CListModeDataPETSIRD::get_empty_record_sptr() const { - shared_ptr sptr(new CListRecordPETSIRD); + shared_ptr sptr(new CListRecordPETSIRD(scanner_info)); std::dynamic_pointer_cast(sptr)->event().set_scanner_sptr( this->get_proj_data_info_sptr()->get_scanner_sptr()); std::dynamic_pointer_cast(sptr)->event().set_PETSIRD_ranges(numberOfModules, numberOfElementsIndices); @@ -563,7 +562,7 @@ CListModeDataPETSIRD::get_next_record(CListRecord& record_of_general_type) const auto& record = dynamic_cast(record_of_general_type); const auto& prompt_list = curr_event_block.prompt_events.at(0).at(0); // TODO: support mulitple pairs of modules. - const auto& delayed_list = m_has_delayeds ? curr_event_block.delayed_events->at(0).at(0) : prompt_list; + const auto& delayed_list = m_has_delayeds ? curr_event_block.delayed_events.at(0).at(0) : prompt_list; const auto& event_list = curr_is_prompt ? prompt_list : delayed_list; From 39b588e665256f1980ec52cc3f5394d4fe78f1e8 Mon Sep 17 00:00:00 2001 From: Nikos Efthimiou Date: Mon, 10 Nov 2025 10:42:27 +0900 Subject: [PATCH 34/42] update PETSIRD for xtensor fix --- PETSIRD | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/PETSIRD b/PETSIRD index fa2f04321c..763832916b 160000 --- a/PETSIRD +++ b/PETSIRD @@ -1 +1 @@ -Subproject commit fa2f04321cba17233817062d64251ed4df72caf9 +Subproject commit 763832916bab240e4ca8406ce23d1d274bf6852a From 3b854d33ebf2215c5432b835a45cebc772d17c2d Mon Sep 17 00:00:00 2001 From: Nikos Efthimiou Date: Thu, 13 Nov 2025 16:39:34 +0800 Subject: [PATCH 35/42] Reconstructs with nonTOF sinograms from PETSIRD --- src/CMakeLists.txt | 2 +- src/IO/CMakeLists.txt | 2 +- .../stir/listmode/CListRecordPETSIRD.h | 30 +++--- .../stir/listmode/CListRecordPETSIRD.inl | 65 ++++++++++--- .../CListModeDataPETSIRD.cxx | 95 +++++++++++++++---- src/listmode_buildblock/CMakeLists.txt | 2 +- 6 files changed, 150 insertions(+), 46 deletions(-) diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt index f42b1a64d8..7496c50bb5 100644 --- a/src/CMakeLists.txt +++ b/src/CMakeLists.txt @@ -254,7 +254,7 @@ endif() # Warning: dependencies for object libraries are not transitive! add_library(stir_registries OBJECT ${STIR_REGISTRIES}) target_link_libraries(stir_registries PRIVATE fmt) -target_link_libraries(stir_registries PRIVATE petsird) +#target_link_libraries(stir_registries PRIVATE petsird) # TODO, really should use stir_libs.cmake target_include_directories(stir_registries PRIVATE ${STIR_INCLUDE_DIR}) diff --git a/src/IO/CMakeLists.txt b/src/IO/CMakeLists.txt index 94afd69f69..74e61ef675 100644 --- a/src/IO/CMakeLists.txt +++ b/src/IO/CMakeLists.txt @@ -184,7 +184,7 @@ if (HAVE_PETSIRD) # # needed for helpers # target_include_directories(IO PUBLIC ${PETSIRD_dir}/..) # target_include_directories(IO PUBLIC ${PETSIRD_dir}/../helpers/include) -target_link_libraries(IO PUBLIC petsird_helpers) +# target_link_libraries(IO PUBLIC petsird_helpers) endif() diff --git a/src/include/stir/listmode/CListRecordPETSIRD.h b/src/include/stir/listmode/CListRecordPETSIRD.h index 85e722dc22..204d2c7bd2 100644 --- a/src/include/stir/listmode/CListRecordPETSIRD.h +++ b/src/include/stir/listmode/CListRecordPETSIRD.h @@ -66,7 +66,7 @@ class CListEventPETSIRD : public CListEvent inline LORAs2Points get_LOR() const override; //! Override the default implementation - // inline void get_bin(Bin& bin, const ProjDataInfo& proj_data_info) const override; + inline void get_bin(Bin& bin, const ProjDataInfo& proj_data_info) const override; inline void set_map_sptr(shared_ptr new_map_sptr) { map_sptr = new_map_sptr; } /*! Set the scanner */ @@ -93,7 +93,9 @@ class CListEventPETSIRD : public CListEvent int numberOfElementsIndices; - std::pair det_0, det_1; + petsird_helpers::ExpandedDetectionBin exp_det_0, exp_det_1; + + inline stir::DetectionPosition<> get_stir_det_pos_from_PETSIRD_id(const petsird_helpers::ExpandedDetectionBin& exp_det_bin) const; private: shared_ptr map_sptr; @@ -119,8 +121,10 @@ class CListTimePETSIRD : public ListTime class CListRecordPETSIRD : public CListRecord { public: - CListRecordPETSIRD(shared_ptr scanner_info) - : scanner_info(scanner_info) {} + CListRecordPETSIRD(shared_ptr scanner_info, + shared_ptr scanner_sptr) + : scanner_info(scanner_info), + this_scanner_sptr(scanner_sptr) {} // ~CListRecordPETSIRD() override {} @@ -151,18 +155,19 @@ class CListRecordPETSIRD : public CListRecord // help expand detection //const ScannerInformation& scanner, const TypeOfModule& type_of_module, const T& list_of_detection_bins - auto dd = petsird_helpers::expand_detection_bin(*scanner_info, + event_data.exp_det_0 = petsird_helpers::expand_detection_bin(*scanner_info, 0, data.detection_bins[0]); - event_data.det_0 = { dd.element_index, dd.module_index }; - std::cout << "Expanded 1: " << dd.module_index << " " << dd.element_index << std::endl; - auto ee = petsird_helpers::expand_detection_bin(*scanner_info, + + // event_data.exp_det_0.first = dd.element_index; + // event_data.exp_det_0.second = dd.module_index; + // std::cout << "Expanded 1: " << event_data.exp_det_0.module_index << " " << event_data.exp_det_0.element_index << std::endl; + event_data.exp_det_1 = petsird_helpers::expand_detection_bin(*scanner_info, 0, data.detection_bins[1]); - event_data.det_1 = { ee.element_index, ee.module_index }; - std::cout << "Expanded 2: " << ee.module_index << " " << ee.element_index << std::endl; - - //(data.detection_bins, event_data.numberOfElementsIndices, event_data.numberOfModules); + // event_data.det_1.first = ee.element_index; + // event_data.det_1.second = ee.module_index; + // std::cout << "Expanded 2: " << event_data.exp_det_1.module_index << " " << event_data.exp_det_1.element_index << std::endl; // event_data.det_0 // = decodeElementAndModuleIndex(data.detection_bins[0], 0, event_data.numberOfElementsIndices, event_data.numberOfModules); @@ -180,6 +185,7 @@ class CListRecordPETSIRD : public CListRecord CListTimePETSIRD time_data; shared_ptr scanner_info;// = header.scanner; + shared_ptr this_scanner_sptr; }; END_NAMESPACE_STIR diff --git a/src/include/stir/listmode/CListRecordPETSIRD.inl b/src/include/stir/listmode/CListRecordPETSIRD.inl index 169c7aef50..fc723a21c5 100644 --- a/src/include/stir/listmode/CListRecordPETSIRD.inl +++ b/src/include/stir/listmode/CListRecordPETSIRD.inl @@ -49,32 +49,73 @@ START_NAMESPACE_STIR +stir::DetectionPosition<> +CListEventPETSIRD::get_stir_det_pos_from_PETSIRD_id(const petsird_helpers::ExpandedDetectionBin& exp_det_bin) const +{ + const auto NUM_MODULES_ALONG_AXIS = scanner_sptr->get_num_axial_blocks(); + const std::array NUM_CRYSTALS_PER_MODULE{ + static_cast(scanner_sptr->get_num_detector_layers()), // N0 (layers) + static_cast(scanner_sptr->get_num_transaxial_crystals_per_block()),// N1 (tx per block) + static_cast(scanner_sptr->get_num_axial_crystals_per_block()) // N2 (ax per block) + }; + + const auto ax_mod = exp_det_bin.module_index % NUM_MODULES_ALONG_AXIS; + const auto tang_mod = exp_det_bin.module_index / NUM_MODULES_ALONG_AXIS; + + const int N0 = static_cast(NUM_CRYSTALS_PER_MODULE[0]); + const int N1 = static_cast(NUM_CRYSTALS_PER_MODULE[1]); + const int N2 = static_cast(NUM_CRYSTALS_PER_MODULE[2]); + + std::array inds; // [layer, transaxial, axial] within the block + int id = static_cast(exp_det_bin.element_index); + + // -------- Row-wise de-linearization (transaxial varies fastest) -------- + // Previous code did: axial first (inds[2] = id % N2; id /= N2; inds[1] = id % N1; ...) + // Change to: transaxial first, then axial. + inds[1] = id % N1; // transaxial within the block + id /= N1; + inds[2] = id % N2; // axial within the block + id /= N2; + inds[0] = id; // layer + // ---------------------------------------------------------------------- + + const stir::DetectionPosition<> pos( + inds[1] + tang_mod * N1, // global transaxial crystal index + inds[2] + ax_mod * N2, // global axial crystal index + inds[0] // layer + ); + return pos; +} + LORAs2Points CListEventPETSIRD::get_LOR() const { LORAs2Points lor; DetectionPositionPair<> det_pos_pair; - // this->get_data().get_detection_position_pair(det_pos_pair); - std::cout << det_pos_pair.pos1().axial_coord() << " " << det_pos_pair.pos1().radial_coord() << " " << det_pos_pair.pos1().tangential_coord() << std::endl; - std::cout << det_pos_pair.pos2().axial_coord() << " " << det_pos_pair.pos2().radial_coord() << " " << det_pos_pair.pos2().tangential_coord() << std::endl; + det_pos_pair.pos1() = get_stir_det_pos_from_PETSIRD_id(exp_det_0); + det_pos_pair.pos2() = get_stir_det_pos_from_PETSIRD_id(exp_det_1); + lor.p1() = map_to_use().get_coordinate_for_index(det_pos_pair.pos1()); lor.p2() = map_to_use().get_coordinate_for_index(det_pos_pair.pos2()); - std::cout << "lor_p1" << lor.p1().x() << " " << lor.p1().y() << " " << lor.p1().z() << std::endl; - std::cout << "lor_p2" < det_pos_pair; -// this->get_data().get_detection_position_pair(det_pos_pair); + DetectionPositionPair<> det_pos_pair; + det_pos_pair.pos1() = get_stir_det_pos_from_PETSIRD_id(exp_det_0); + det_pos_pair.pos2() = get_stir_det_pos_from_PETSIRD_id(exp_det_1); + // this->get_data().get_detection_position_pair(det_pos_pair); + dynamic_cast(proj_data_info).get_bin_for_det_pos_pair(bin, det_pos_pair); +} -// int nikos = 0; -// } END_NAMESPACE_STIR diff --git a/src/listmode_buildblock/CListModeDataPETSIRD.cxx b/src/listmode_buildblock/CListModeDataPETSIRD.cxx index 03222bedee..bbb00d73f6 100644 --- a/src/listmode_buildblock/CListModeDataPETSIRD.cxx +++ b/src/listmode_buildblock/CListModeDataPETSIRD.cxx @@ -359,7 +359,70 @@ CListModeDataPETSIRD::isCylindricalConfiguration(const std::vectorenergy_resolution_at_511.front(), // energy_resolution_v + 511, // reference_energy_v + tof_bin_edges.NumberOfBins() + 1, + (tof_bin_edges.edges[1] - tof_bin_edges.edges[0])*10/2, + *unique_tof_values.begin() * 10 // non-TOF + )); + + // 13, + // 4.056 * 1000 / 13, + // 555.F); // TODO singles info incorrect + + + // /* maximum number of timing bins */ + // tof_bin_edges.NumberOfBins(), + // /* size of basic TOF bin */ + // 10, + // /* Scanner's timing resolution */ + // *unique_tof_values.begin())); + return true; + } + else + { + info("the cylindical area is less then 95% matching the polygon area. We will predsume a non-cylindrical configuration."); + this_scanner_sptr.reset( new Scanner(Scanner::User_defined_scanner, std::string("PETSIRD_defined_scanner"), /* num dets per ring */ @@ -373,9 +436,9 @@ CListModeDataPETSIRD::isCylindricalConfiguration(const std::vector(curr_time_block); else error("CListModeDataPETSIRD: holds_alternative not true. Abord."); - isCylindricalConfiguration(replicated_module_list); - bool b = false; - if (b) // isCylindricalConfiguration(scanner_info, replicated_module_list)) + + if ( isCylindricalConfiguration(replicated_module_list)) { int tof_mash_factor = 1; this->set_proj_data_info_sptr(std::const_pointer_cast( @@ -491,15 +548,15 @@ CListModeDataPETSIRD::CListModeDataPETSIRD(const std::string& listmode_filename, for (auto& corner : box_shape.corners) { // if STIR (z,y,x) -> PETSIRD (-y, -x, z) pheraps the order below needs to be changed - mean_coord.x() = +corner.c[0] / box_shape.corners.size(); - mean_coord.y() = +corner.c[1] / box_shape.corners.size(); + mean_coord.x() = +corner.c[0] / box_shape.corners.size() * 10 ; + mean_coord.y() = +corner.c[1] / box_shape.corners.size() * 10 ; mean_coord.z() = +corner.c[2] / box_shape.corners.size(); } // save mean pos into map petsird_map[detpos] = mean_coord; - std::cout << detpos.radial_coord() << "," << detpos.axial_coord() << "," << detpos.tangential_coord() << "," - << mean_coord.x() << "," << mean_coord.y() << "," << mean_coord.z() << "," << std::endl; + std::cout << "NIKOSSS " << detpos.radial_coord() << ", " << detpos.axial_coord() << ", " << detpos.tangential_coord() << ", " + << mean_coord.x() << ", " << mean_coord.y() << ", " << mean_coord.z() << ", " << std::endl; } this->map.reset(new DetectorCoordinateMap(petsird_map)); @@ -547,7 +604,7 @@ CListModeDataPETSIRD::open_lm_file() const shared_ptr CListModeDataPETSIRD::get_empty_record_sptr() const { - shared_ptr sptr(new CListRecordPETSIRD(scanner_info)); + shared_ptr sptr(new CListRecordPETSIRD(scanner_info, this_scanner_sptr)); std::dynamic_pointer_cast(sptr)->event().set_scanner_sptr( this->get_proj_data_info_sptr()->get_scanner_sptr()); std::dynamic_pointer_cast(sptr)->event().set_PETSIRD_ranges(numberOfModules, numberOfElementsIndices); diff --git a/src/listmode_buildblock/CMakeLists.txt b/src/listmode_buildblock/CMakeLists.txt index 41853b14c0..5b3719b464 100644 --- a/src/listmode_buildblock/CMakeLists.txt +++ b/src/listmode_buildblock/CMakeLists.txt @@ -97,5 +97,5 @@ if (HAVE_PETSIRD) # $ # ) -target_link_libraries(listmode_buildblock PUBLIC petsird_helpers) +# target_link_libraries(listmode_buildblock PUBLIC petsird_helpers) endif() From 570a9e6e192ea3f67fd34fd2b0e62f198d7f8904 Mon Sep 17 00:00:00 2001 From: NikEfth Date: Tue, 18 Nov 2025 11:44:35 -0500 Subject: [PATCH 36/42] Fixed all the CMake issues with PETSIRD integration. --- CMakeLists.txt | 78 ++++++++++++++------------ src/IO/CMakeLists.txt | 54 ++++-------------- src/listmode_buildblock/CMakeLists.txt | 35 +++--------- 3 files changed, 64 insertions(+), 103 deletions(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index fa9ad268f0..62f4594884 100755 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -261,55 +261,63 @@ else() endif() if(NOT DISABLE_PETSIRD) + if(GIT_FOUND AND EXISTS "${PROJECT_SOURCE_DIR}/.git") - # Check if PETSIRD is already registered as a submodule + # Check if PETSIRD is already registered as a submodule + execute_process( + COMMAND ${GIT_EXECUTABLE} config --file ${PROJECT_SOURCE_DIR}/.gitmodules --get-regexp "submodule\\.PETSIRD\\.path" + RESULT_VARIABLE SUBMOD_EXISTS + OUTPUT_QUIET + ERROR_QUIET + ) + + if(NOT SUBMOD_EXISTS EQUAL 0) + message(STATUS "Adding PETSIRD submodule...") execute_process( - COMMAND ${GIT_EXECUTABLE} config --file ${PROJECT_SOURCE_DIR}/.gitmodules --get-regexp "submodule\\.PETSIRD\\.path" - RESULT_VARIABLE SUBMOD_EXISTS - OUTPUT_QUIET - ERROR_QUIET + COMMAND ${GIT_EXECUTABLE} submodule add https://github.com/ETSInitiative/PETSIRD.git PETSIRD + WORKING_DIRECTORY ${PROJECT_SOURCE_DIR} + RESULT_VARIABLE GIT_ADD_RESULT ) - - if(NOT SUBMOD_EXISTS EQUAL 0) - message(STATUS "Adding PETSIRD submodule...") - execute_process( - COMMAND ${GIT_EXECUTABLE} submodule add https://github.com/ETSInitiative/PETSIRD.git PETSIRD - WORKING_DIRECTORY ${PROJECT_SOURCE_DIR} - RESULT_VARIABLE GIT_ADD_RESULT - ) - if(NOT GIT_ADD_RESULT EQUAL 0) - message(WARNING "Submodule add failed with code ${GIT_ADD_RESULT}") - endif() - else() - message(STATUS "PETSIRD submodule already exists in .gitmodules.") + if(NOT GIT_ADD_RESULT EQUAL 0) + message(WARNING "Submodule add failed with code ${GIT_ADD_RESULT}") endif() + else() + message(STATUS "PETSIRD submodule already exists in .gitmodules.") + endif() - # Always update/init to ensure it's ready - execute_process( - COMMAND ${GIT_EXECUTABLE} submodule update --init --recursive - WORKING_DIRECTORY ${PROJECT_SOURCE_DIR} - ) - endif() + # Optionally ensure the submodule is checked out + # execute_process( + # COMMAND ${GIT_EXECUTABLE} submodule update --init --recursive PETSIRD + # WORKING_DIRECTORY ${PROJECT_SOURCE_DIR} + # ) - execute_process( + # Build/generate PETSIRD C++ code + execute_process( COMMAND just generate WORKING_DIRECTORY ${PROJECT_SOURCE_DIR}/PETSIRD RESULT_VARIABLE JUST_RESULT - ) + ) - if(JUST_RESULT EQUAL 0) + if(JUST_RESULT EQUAL 0) set(HAVE_PETSIRD TRUE) - set(PETSIRD_base_dir ${PROJECT_SOURCE_DIR}/PETSIRD/cpp/generated) + set(PETSIRD_base_dir "${PROJECT_SOURCE_DIR}/PETSIRD/cpp/generated") + set(PETSIRD_dir "${PROJECT_SOURCE_DIR}/PETSIRD/cpp") + add_subdirectory(${PETSIRD_dir} PETSIRD) + install(TARGETS petsird_generated + EXPORT STIRTargets + DESTINATION lib) message(STATUS "PETSIRD generation succeeded. THE PATH is ${PETSIRD_base_dir}. HAVE_PETSIRD set to TRUE.") - else() + else() set(HAVE_PETSIRD FALSE) - message(WARNING "PETSIRD generation failed with code ${JUST_RESULT}. HAVE_PETSIRD not set.") + message(WARNING "PETSIRD generation failed with code ${JUST_RESULT}. HAVE_PETSIRD set to FALSE.") + endif() + + else() + # No git or no .git dir -> we can't auto-manage the submodule + set(HAVE_PETSIRD FALSE) + message(STATUS "Git not found or no .git directory; building STIR without PETSIRD support.") endif() - set(PETSIRD_dir ${PROJECT_SOURCE_DIR}/PETSIRD/cpp) - add_subdirectory(${PETSIRD_dir} PETSIRD) - #install(TARGETS petsird_generated - # EXPORT STIRTargets - # DESTINATION lib) + endif() #### enable support for ctest diff --git a/src/IO/CMakeLists.txt b/src/IO/CMakeLists.txt index 74e61ef675..7521ac1056 100644 --- a/src/IO/CMakeLists.txt +++ b/src/IO/CMakeLists.txt @@ -111,6 +111,17 @@ if (HAVE_HDF5) target_link_libraries(IO PUBLIC ${HDF5_CXX_LIBRARIES}) endif() +if(HAVE_PETSIRD) +target_include_directories(IO + PRIVATE + ${PROJECT_SOURCE_DIR}/PETSIRD/cpp/generated +) +target_link_libraries(IO + PRIVATE + petsird_generated + ) +endif() + if (HAVE_ITK) target_link_libraries(IO PRIVATE ITKCommon ${ITK_LIBRARIES}) endif() @@ -144,48 +155,7 @@ if (NOT MINI_STIR) target_link_libraries(IO PUBLIC listmode_buildblock) endif() -if (HAVE_PETSIRD) -# #set(PETSIRD_dir ../../PETSIRD/cpp/generated) - - -#target_include_directories(IO PUBLIC -# $ -# $ -# $ -#) - - -# # # target_include_directories(IO PUBLIC -# # # $ -# # # $ -# # # ) - -# # # target_include_directories(IO PUBLIC -# # # $ -# # # $ -# # # ) -# # target_include_directories(IO PUBLIC -# # $ -# # $ -# # $ -# # $ -# # ) - -# # target_link_libraries(IO PUBLIC petsird_generated) - -# set(PETSIRD_dir ../../PETSIRD/cpp/generated) -# add_subdirectory(${PETSIRD_dir} PETSIRD_generated) - -# install(TARGETS petsird_generated -# EXPORT STIRTargets -# DESTINATION lib) - -# target_include_directories(IO PUBLIC ${PETSIRD_dir}) -# # needed for helpers -# target_include_directories(IO PUBLIC ${PETSIRD_dir}/..) -# target_include_directories(IO PUBLIC ${PETSIRD_dir}/../helpers/include) -# target_link_libraries(IO PUBLIC petsird_helpers) -endif() + diff --git a/src/listmode_buildblock/CMakeLists.txt b/src/listmode_buildblock/CMakeLists.txt index 5b3719b464..8ea5cb9665 100644 --- a/src/listmode_buildblock/CMakeLists.txt +++ b/src/listmode_buildblock/CMakeLists.txt @@ -72,30 +72,13 @@ if (HAVE_HDF5) endif() if (HAVE_PETSIRD) -#set(PETSIRD_dir ../../PETSIRD/cpp/generated) -#add_subdirectory(${PETSIRD_dir} PETSIRD_generated) - -#target_include_directories(listmode_buildblock PUBLIC -# $ -# $ -# $ -# $ -#) - -# target_include_directories(listmode_buildblock PUBLIC -# $ -# $ -# ) - -# target_include_directories(listmode_buildblock PUBLIC -# $ -# $ -# ) - -# target_include_directories(listmode_buildblock PUBLIC -# $ -# $ -# ) - -# target_link_libraries(listmode_buildblock PUBLIC petsird_helpers) +target_include_directories(listmode_buildblock + PRIVATE + ${PROJECT_SOURCE_DIR}/PETSIRD/cpp/helpers/include + ${PROJECT_SOURCE_DIR}/PETSIRD/cpp/generated +) +target_link_libraries(listmode_buildblock + PRIVATE + petsird_generated + ) endif() From 185afde3117691eb85e7df672055cd86795aecb8 Mon Sep 17 00:00:00 2001 From: NikEfth Date: Fri, 21 Nov 2025 15:50:37 +0100 Subject: [PATCH 37/42] This should fix all linking issues with PETSIRD in STIR. --- CMakeLists.txt | 70 ++++++++------------------ src/IO/CMakeLists.txt | 19 +++---- src/listmode_buildblock/CMakeLists.txt | 8 +-- 3 files changed, 33 insertions(+), 64 deletions(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index 62f4594884..533521edb2 100755 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -261,61 +261,35 @@ else() endif() if(NOT DISABLE_PETSIRD) - - if(GIT_FOUND AND EXISTS "${PROJECT_SOURCE_DIR}/.git") - # Check if PETSIRD is already registered as a submodule - execute_process( - COMMAND ${GIT_EXECUTABLE} config --file ${PROJECT_SOURCE_DIR}/.gitmodules --get-regexp "submodule\\.PETSIRD\\.path" - RESULT_VARIABLE SUBMOD_EXISTS - OUTPUT_QUIET - ERROR_QUIET - ) - - if(NOT SUBMOD_EXISTS EQUAL 0) - message(STATUS "Adding PETSIRD submodule...") + if(GIT_FOUND AND EXISTS "${PROJECT_SOURCE_DIR}/.git") + # Initialize PETSIRD submodule if not present + if(NOT EXISTS "${PROJECT_SOURCE_DIR}/PETSIRD/CMakeLists.txt") + message(STATUS "Initializing PETSIRD submodule...") execute_process( - COMMAND ${GIT_EXECUTABLE} submodule add https://github.com/ETSInitiative/PETSIRD.git PETSIRD + COMMAND ${GIT_EXECUTABLE} submodule update --init --recursive PETSIRD WORKING_DIRECTORY ${PROJECT_SOURCE_DIR} - RESULT_VARIABLE GIT_ADD_RESULT + RESULT_VARIABLE GIT_SUBMOD_RESULT ) - if(NOT GIT_ADD_RESULT EQUAL 0) - message(WARNING "Submodule add failed with code ${GIT_ADD_RESULT}") + if(NOT GIT_SUBMOD_RESULT EQUAL 0) + message(FATAL_ERROR "Failed to initialize PETSIRD submodule") endif() - else() - message(STATUS "PETSIRD submodule already exists in .gitmodules.") - endif() - - # Optionally ensure the submodule is checked out - # execute_process( - # COMMAND ${GIT_EXECUTABLE} submodule update --init --recursive PETSIRD - # WORKING_DIRECTORY ${PROJECT_SOURCE_DIR} - # ) - - # Build/generate PETSIRD C++ code - execute_process( - COMMAND just generate - WORKING_DIRECTORY ${PROJECT_SOURCE_DIR}/PETSIRD - RESULT_VARIABLE JUST_RESULT - ) - - if(JUST_RESULT EQUAL 0) - set(HAVE_PETSIRD TRUE) - set(PETSIRD_base_dir "${PROJECT_SOURCE_DIR}/PETSIRD/cpp/generated") - set(PETSIRD_dir "${PROJECT_SOURCE_DIR}/PETSIRD/cpp") - add_subdirectory(${PETSIRD_dir} PETSIRD) - install(TARGETS petsird_generated - EXPORT STIRTargets - DESTINATION lib) - message(STATUS "PETSIRD generation succeeded. THE PATH is ${PETSIRD_base_dir}. HAVE_PETSIRD set to TRUE.") - else() - set(HAVE_PETSIRD FALSE) - message(WARNING "PETSIRD generation failed with code ${JUST_RESULT}. HAVE_PETSIRD set to FALSE.") endif() + endif() + # Check if PETSIRD directory exists + if(EXISTS "${PROJECT_SOURCE_DIR}/PETSIRD/cpp/CMakeLists.txt") + set(HAVE_PETSIRD TRUE) + set(PETSIRD_dir ${PROJECT_SOURCE_DIR}/PETSIRD/cpp) + add_subdirectory(${PETSIRD_dir} PETSIRD) + # Make PETSIRD headers globally available + include_directories(${PROJECT_SOURCE_DIR}/PETSIRD/cpp/generated) + include_directories(${PROJECT_SOURCE_DIR}/PETSIRD/cpp/helpers/include) + install(TARGETS petsird_generated + EXPORT STIRTargets + DESTINATION lib) + message(STATUS "PETSIRD support enabled") else() - # No git or no .git dir -> we can't auto-manage the submodule - set(HAVE_PETSIRD FALSE) - message(STATUS "Git not found or no .git directory; building STIR without PETSIRD support.") + message(FATAL_ERROR "PETSIRD directory not found. Please run: git submodule update --init --recursive") endif() endif() diff --git a/src/IO/CMakeLists.txt b/src/IO/CMakeLists.txt index 7521ac1056..6a609236b7 100644 --- a/src/IO/CMakeLists.txt +++ b/src/IO/CMakeLists.txt @@ -111,17 +111,6 @@ if (HAVE_HDF5) target_link_libraries(IO PUBLIC ${HDF5_CXX_LIBRARIES}) endif() -if(HAVE_PETSIRD) -target_include_directories(IO - PRIVATE - ${PROJECT_SOURCE_DIR}/PETSIRD/cpp/generated -) -target_link_libraries(IO - PRIVATE - petsird_generated - ) -endif() - if (HAVE_ITK) target_link_libraries(IO PRIVATE ITKCommon ${ITK_LIBRARIES}) endif() @@ -155,7 +144,13 @@ if (NOT MINI_STIR) target_link_libraries(IO PUBLIC listmode_buildblock) endif() - +if(HAVE_PETSIRD) + target_include_directories(IO + PUBLIC + $ + ) + target_link_libraries(IO PUBLIC petsird_generated) +endif() diff --git a/src/listmode_buildblock/CMakeLists.txt b/src/listmode_buildblock/CMakeLists.txt index 8ea5cb9665..1168a8b514 100644 --- a/src/listmode_buildblock/CMakeLists.txt +++ b/src/listmode_buildblock/CMakeLists.txt @@ -73,12 +73,12 @@ endif() if (HAVE_PETSIRD) target_include_directories(listmode_buildblock - PRIVATE - ${PROJECT_SOURCE_DIR}/PETSIRD/cpp/helpers/include - ${PROJECT_SOURCE_DIR}/PETSIRD/cpp/generated + PUBLIC +$ + $ ) target_link_libraries(listmode_buildblock - PRIVATE + PUBLIC petsird_generated ) endif() From 690850f43a8c8e556a1103512b5e85cbed2b6f00 Mon Sep 17 00:00:00 2001 From: NikEfth Date: Tue, 25 Nov 2025 17:33:28 +0100 Subject: [PATCH 38/42] This commit is unstable. --- src/buildblock/ProjDataInfo.cxx | 144 ++++----- src/include/stir/Scanner.h | 3 +- .../stir/listmode/CListModeDataPETSIRD.h | 32 ++ .../stir/listmode/CListRecordPETSIRD.h | 43 +-- .../stir/listmode/CListRecordPETSIRD.inl | 97 +++--- .../CListModeDataPETSIRD.cxx | 303 +++++++++++++++--- .../find_basic_vs_nums_in_subset.cxx | 4 +- 7 files changed, 420 insertions(+), 206 deletions(-) diff --git a/src/buildblock/ProjDataInfo.cxx b/src/buildblock/ProjDataInfo.cxx index b72804f35b..be7e90679b 100644 --- a/src/buildblock/ProjDataInfo.cxx +++ b/src/buildblock/ProjDataInfo.cxx @@ -169,94 +169,82 @@ 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) - { - tof_mash_factor = new_num; - if (tof_mash_factor > scanner_ptr->get_max_num_timing_poss()) - error("ProjDataInfo::set_tof_mash_factor: TOF mashing factor (" + std::to_string(tof_mash_factor) - + +") must be smaller than or equal to the scanner's number of max timing bins (" - + std::to_string(scanner_ptr->get_max_num_timing_poss()) + ")."); - -#if 0 - // KT: code disabled as buggy but currently not needed - tof_increament_in_mm = tof_delta_time_to_mm(scanner_ptr->get_size_of_timing_pos()); - min_unmashed_tof_pos_num = - (scanner_ptr->get_max_num_timing_poss())/2; - max_unmashed_tof_pos_num = min_unmashed_tof_pos_num + (scanner_ptr->get_max_num_timing_poss()) -1; - - // Upper and lower boundaries of the timing poss; - tof_bin_unmashed_boundaries_mm.grow(min_unmashed_tof_pos_num, max_unmashed_tof_pos_num); - tof_bin_unmashed_boundaries_ps.grow(min_unmashed_tof_pos_num, max_unmashed_tof_pos_num); - - // Silently intialise the unmashed TOF bins. - for (int k = min_unmashed_tof_pos_num; k <= max_unmashed_tof_pos_num; ++k ) - { - Bin bin; - bin.timing_pos_num() = k; - // if we ever re-enable this code, there is a BUG here: - // get_k relies on num_tof_bins, so this should have been set to the unmashed value from the scanner - float cur_low = get_k(bin) - get_sampling_in_k(bin)/2.f; - float cur_high = get_k(bin) + get_sampling_in_k(bin)/2.f; - - tof_bin_unmashed_boundaries_mm[k].low_lim = cur_low; - tof_bin_unmashed_boundaries_mm[k].high_lim = cur_high; - tof_bin_unmashed_boundaries_ps[k].low_lim = static_cast(mm_to_tof_delta_time(tof_bin_unmashed_boundaries_mm[k].low_lim)); - tof_bin_unmashed_boundaries_ps[k].high_lim = static_cast(mm_to_tof_delta_time(tof_bin_unmashed_boundaries_mm[k].high_lim)); + const bool tof_ready = scanner_ptr->is_tof_ready(); + + // Non-TOF mode (either scanner not TOF-ready or invalid mash factor) + if (!tof_ready || new_num <= 0) + { + 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 projector + // won't use any boundary conditions + return; + } + + const int max_timing_poss = scanner_ptr->get_max_num_timing_poss(); + + if (new_num > max_timing_poss) + { + error("ProjDataInfo::set_tof_mash_factor: TOF mashing factor (" + + std::to_string(new_num) + + ") must be smaller than or equal to the scanner's number of " + "max timing bins (" + + std::to_string(max_timing_poss) + ")."); + } + + tof_mash_factor = new_num; + // Initialise mashed TOF bins +tof_increament_in_mm = + tof_delta_time_to_mm(tof_mash_factor * scanner_ptr->get_size_of_timing_pos()); + +const int num_mashed_bins = max_timing_poss / tof_mash_factor; +num_tof_bins = num_mashed_bins; + +// Compute min/max TOF position numbers without enforcing odd count +// +// We choose a symmetric convention: +// - If num_tof_bins is odd: bins go from -N/2 ... +N/2 +// - If num_tof_bins is even: bins go from -(N/2) ... +(N/2 - 1) +// This preserves the old behavior for odd counts and gives clean indexing for even counts. +min_tof_pos_num = -num_tof_bins / 2; +max_tof_pos_num = min_tof_pos_num + num_tof_bins - 1; - } -#endif - // Now, initialise the mashed TOF bins. - tof_increament_in_mm = tof_delta_time_to_mm(tof_mash_factor * scanner_ptr->get_size_of_timing_pos()); +// Upper and lower boundaries of the timing positions +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); - // TODO cope with even numbers! - min_tof_pos_num = -(scanner_ptr->get_max_num_timing_poss() / tof_mash_factor) / 2; - max_tof_pos_num = min_tof_pos_num + (scanner_ptr->get_max_num_timing_poss() / tof_mash_factor) - 1; +for (int k = min_tof_pos_num; k <= max_tof_pos_num; ++k) +{ + Bin bin; + bin.timing_pos_num() = k; - num_tof_bins = max_tof_pos_num - min_tof_pos_num + 1; + const float sampling = get_sampling_in_k(bin); + const float center = get_k(bin); - // 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."); + const float cur_low = center - sampling / 2.f; + const float cur_high = center + sampling / 2.f; - // Upper and lower boundaries of the timing poss; - tof_bin_boundaries_mm.grow(min_tof_pos_num, max_tof_pos_num); + tof_bin_boundaries_mm[k].low_lim = cur_low; + tof_bin_boundaries_mm[k].high_lim = cur_high; - tof_bin_boundaries_ps.grow(min_tof_pos_num, max_tof_pos_num); + tof_bin_boundaries_ps[k].low_lim = + static_cast(mm_to_tof_delta_time(cur_low)); + tof_bin_boundaries_ps[k].high_lim = + static_cast(mm_to_tof_delta_time(cur_high)); - for (int k = min_tof_pos_num; k <= max_tof_pos_num; ++k) - { - Bin bin; - bin.timing_pos_num() = k; - - float cur_low = get_k(bin) - get_sampling_in_k(bin) / 2.f; - float cur_high = get_k(bin) + get_sampling_in_k(bin) / 2.f; - - tof_bin_boundaries_mm[k].low_lim = cur_low; - tof_bin_boundaries_mm[k].high_lim = cur_high; - tof_bin_boundaries_ps[k].low_lim = static_cast(mm_to_tof_delta_time(tof_bin_boundaries_mm[k].low_lim)); - tof_bin_boundaries_ps[k].high_lim = static_cast(mm_to_tof_delta_time(tof_bin_boundaries_mm[k].high_lim)); - // I could imagine a better printing. - info(format("Tbin {}: {} - {} mm ({} - {} ps) = {}", - 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 - } + info(format("Tbin {}: {} - {} mm ({} - {} ps) = {}", + 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, + sampling)); +} } std::vector diff --git a/src/include/stir/Scanner.h b/src/include/stir/Scanner.h index 5b2210c694..3ddd69be35 100644 --- a/src/include/stir/Scanner.h +++ b/src/include/stir/Scanner.h @@ -574,6 +574,8 @@ class Scanner shared_ptr get_detector_map_sptr() const { return detector_map_sptr; } + void set_detector_map(const DetectorCoordinateMap::det_pos_to_coord_type& coord_map); + private: bool _already_setup; Type type; @@ -628,7 +630,6 @@ class Scanner std::string crystal_map_file_name; shared_ptr detector_map_sptr; /*! effective detection positions including average DOI */ - void set_detector_map(const DetectorCoordinateMap::det_pos_to_coord_type& coord_map); void initialise_max_FOV_radius(); // function to create the maps diff --git a/src/include/stir/listmode/CListModeDataPETSIRD.h b/src/include/stir/listmode/CListModeDataPETSIRD.h index d338eb21c8..b7d7268105 100644 --- a/src/include/stir/listmode/CListModeDataPETSIRD.h +++ b/src/include/stir/listmode/CListModeDataPETSIRD.h @@ -49,6 +49,28 @@ START_NAMESPACE_STIR coordinates. */ +struct ExpandedDetectionBinLess +{ + + bool operator()(const petsird::ExpandedDetectionBin& a, + const petsird::ExpandedDetectionBin& b) const + { + // Adjust field names if needed (I assume: module, element, energy_bin) + if (a.module_index < b.module_index) return true; + if (a.module_index > b.module_index) return false; + + if (a.element_index < b.element_index) return true; + if (a.element_index > b.element_index) return false; + return a.energy_index < b.energy_index; + } +}; + +using PETSIRDToSTIRMap = std::map< + petsird::ExpandedDetectionBin, + stir::DetectionPosition<>, + ExpandedDetectionBinLess +>; + class CListModeDataPETSIRD : public CListModeDataBasedOnCoordinateMap { public: @@ -82,8 +104,18 @@ class CListModeDataPETSIRD : public CListModeDataBasedOnCoordinateMap int numberOfElementsIndices; + int blocks_per_bucket_transaxial; + + int blocks_per_bucket_axial; + + int num_axial_crystals_per_block; + + int num_trans_crystals_per_block; + mutable petsird::EventTimeBlock curr_event_block; + shared_ptr petsird_to_stir; + petsird::TypeOfModulePair type_of_module_pair{ 0, 0 }; shared_ptr this_scanner_sptr; diff --git a/src/include/stir/listmode/CListRecordPETSIRD.h b/src/include/stir/listmode/CListRecordPETSIRD.h index 204d2c7bd2..038cc9b3a9 100644 --- a/src/include/stir/listmode/CListRecordPETSIRD.h +++ b/src/include/stir/listmode/CListRecordPETSIRD.h @@ -97,6 +97,8 @@ class CListEventPETSIRD : public CListEvent inline stir::DetectionPosition<> get_stir_det_pos_from_PETSIRD_id(const petsird_helpers::ExpandedDetectionBin& exp_det_bin) const; + shared_ptr petsird_to_stir; + private: shared_ptr map_sptr; shared_ptr scanner_sptr; @@ -122,9 +124,16 @@ class CListRecordPETSIRD : public CListRecord { public: CListRecordPETSIRD(shared_ptr scanner_info, - shared_ptr scanner_sptr) - : scanner_info(scanner_info), - this_scanner_sptr(scanner_sptr) {} + shared_ptr scanner_sptr, + // shared_ptr map_sptr, + shared_ptr map_sptr, + shared_ptr petsird_to_stir) + : scanner_info(scanner_info) + { + event_data.set_scanner_sptr(scanner_sptr); + event_data.set_map_sptr(map_sptr); + event_data.petsird_to_stir = petsird_to_stir; + } // ~CListRecordPETSIRD() override {} @@ -145,37 +154,14 @@ class CListRecordPETSIRD : public CListRecord virtual Succeeded init_from_data(const petsird::CoincidenceEvent& data, bool is_prompt = true) { - // auto decodeElementAndModuleIndex - // = [](int linearIndex, int energyIndex, int numberOfElementsIndices, int numberOfModules) -> std::pair { - // int reduced = (linearIndex - energyIndex) / numberOfModules; - // int moduleIndex = reduced / numberOfElementsIndices; - // int elementIndex = reduced % numberOfElementsIndices; - // return { elementIndex, moduleIndex }; - // }; - - // help expand detection - //const ScannerInformation& scanner, const TypeOfModule& type_of_module, const T& list_of_detection_bins event_data.exp_det_0 = petsird_helpers::expand_detection_bin(*scanner_info, 0, data.detection_bins[0]); - // event_data.exp_det_0.first = dd.element_index; - // event_data.exp_det_0.second = dd.module_index; - // std::cout << "Expanded 1: " << event_data.exp_det_0.module_index << " " << event_data.exp_det_0.element_index << std::endl; event_data.exp_det_1 = petsird_helpers::expand_detection_bin(*scanner_info, 0, data.detection_bins[1]); - // event_data.det_1.first = ee.element_index; - // event_data.det_1.second = ee.module_index; - // std::cout << "Expanded 2: " << event_data.exp_det_1.module_index << " " << event_data.exp_det_1.element_index << std::endl; - - // event_data.det_0 - // = decodeElementAndModuleIndex(data.detection_bins[0], 0, event_data.numberOfElementsIndices, event_data.numberOfModules); - // event_data.det_1 - // = decodeElementAndModuleIndex(data.detection_bins[1], 0, event_data.numberOfElementsIndices, event_data.numberOfModules); - - // std::cout << event_data.det_0.first << " " << event_data.det_0.second << std::endl; - // std::cout << event_data.det_1.first << " " << event_data.det_1.second << std::endl; + event_data.set_prompt(is_prompt); return Succeeded::yes; } @@ -184,8 +170,7 @@ class CListRecordPETSIRD : public CListRecord CListEventPETSIRD event_data; CListTimePETSIRD time_data; - shared_ptr scanner_info;// = header.scanner; - shared_ptr this_scanner_sptr; + shared_ptr scanner_info; }; END_NAMESPACE_STIR diff --git a/src/include/stir/listmode/CListRecordPETSIRD.inl b/src/include/stir/listmode/CListRecordPETSIRD.inl index fc723a21c5..d739486188 100644 --- a/src/include/stir/listmode/CListRecordPETSIRD.inl +++ b/src/include/stir/listmode/CListRecordPETSIRD.inl @@ -27,10 +27,7 @@ \ingroup listmode \brief Inline implementation of class stir::CListEventPETSIRD and stir::CListRecordPETSIRD with supporting classes - \author Jannis Fischer - \author Parisa Khateri - \author Markus Jehl - \author Kris Thielemans + \author Nikos Efthimiou \author Daniel Deidda */ @@ -38,53 +35,28 @@ #include "stir/listmode/CListRecord.h" #include "stir/ProjDataInfo.h" #include "stir/Bin.h" -#include "stir/LORCoordinates.h" -#include "stir/Succeeded.h" - +#include "stir/CartesianCoordinate3D.h" +#include "stir/error.h" #include "stir/ProjDataInfoCylindricalNoArcCorr.h" #include "stir/ProjDataInfoBlocksOnCylindricalNoArcCorr.h" #include "stir/ProjDataInfoGenericNoArcCorr.h" -#include "stir/CartesianCoordinate3D.h" -#include "stir/error.h" START_NAMESPACE_STIR stir::DetectionPosition<> -CListEventPETSIRD::get_stir_det_pos_from_PETSIRD_id(const petsird_helpers::ExpandedDetectionBin& exp_det_bin) const +CListEventPETSIRD::get_stir_det_pos_from_PETSIRD_id(const petsird::ExpandedDetectionBin& exp_det_bin) const { - const auto NUM_MODULES_ALONG_AXIS = scanner_sptr->get_num_axial_blocks(); - const std::array NUM_CRYSTALS_PER_MODULE{ - static_cast(scanner_sptr->get_num_detector_layers()), // N0 (layers) - static_cast(scanner_sptr->get_num_transaxial_crystals_per_block()),// N1 (tx per block) - static_cast(scanner_sptr->get_num_axial_crystals_per_block()) // N2 (ax per block) - }; - - const auto ax_mod = exp_det_bin.module_index % NUM_MODULES_ALONG_AXIS; - const auto tang_mod = exp_det_bin.module_index / NUM_MODULES_ALONG_AXIS; - - const int N0 = static_cast(NUM_CRYSTALS_PER_MODULE[0]); - const int N1 = static_cast(NUM_CRYSTALS_PER_MODULE[1]); - const int N2 = static_cast(NUM_CRYSTALS_PER_MODULE[2]); - - std::array inds; // [layer, transaxial, axial] within the block - int id = static_cast(exp_det_bin.element_index); - - // -------- Row-wise de-linearization (transaxial varies fastest) -------- - // Previous code did: axial first (inds[2] = id % N2; id /= N2; inds[1] = id % N1; ...) - // Change to: transaxial first, then axial. - inds[1] = id % N1; // transaxial within the block - id /= N1; - inds[2] = id % N2; // axial within the block - id /= N2; - inds[0] = id; // layer - // ---------------------------------------------------------------------- - - const stir::DetectionPosition<> pos( - inds[1] + tang_mod * N1, // global transaxial crystal index - inds[2] + ax_mod * N2, // global axial crystal index - inds[0] // layer - ); - return pos; +// const-friendly lookup + auto it = petsird_to_stir->find(exp_det_bin); + if (it == petsird_to_stir->end()) { + // handle missing key however STIR usually does: + // - throw + // - or call error(...) + // - or return a default DetectionPosition + error("get_stir_det_pos_from_PETSIRD_id: PETSIRD id not found in petsird_to_stir map", exp_det_bin.module_index, exp_det_bin.element_index, exp_det_bin.energy_index); + } + + return it->second; // copy of DetectionPosition<> } LORAs2Points @@ -110,11 +82,42 @@ CListEventPETSIRD::get_bin(Bin& bin, const ProjDataInfo& proj_data_info) const { DetectionPositionPair<> det_pos_pair; - det_pos_pair.pos1() = get_stir_det_pos_from_PETSIRD_id(exp_det_0); - det_pos_pair.pos2() = get_stir_det_pos_from_PETSIRD_id(exp_det_1); - // this->get_data().get_detection_position_pair(det_pos_pair); - dynamic_cast(proj_data_info).get_bin_for_det_pos_pair(bin, det_pos_pair); + if(scanner_sptr->get_scanner_geometry() == "Cylindrical") + { + det_pos_pair.pos1() = get_stir_det_pos_from_PETSIRD_id(exp_det_0); + det_pos_pair.pos2() = get_stir_det_pos_from_PETSIRD_id(exp_det_1); + // this->get_data().get_detection_position_pair(det_pos_pair); + dynamic_cast(proj_data_info).get_bin_for_det_pos_pair(bin, det_pos_pair); + } + else + { + if (!map_sptr) + { + std::cerr << "Error: No detector map set in CListEventPETSIRD::get_bin()" << std::endl; + // this->get_data().get_detection_position_pair(det_pos_pair); + } + else{ + DetectionPositionPair<> det_pos_pair; + det_pos_pair.pos1() = get_stir_det_pos_from_PETSIRD_id(exp_det_0); + det_pos_pair.pos2() = get_stir_det_pos_from_PETSIRD_id(exp_det_1); + // std::cout<< exp_det_0.module_index << ", " << exp_det_0.element_index << " ---- " << exp_det_1.module_index << ", " << exp_det_1.element_index << std::endl; + const stir::CartesianCoordinate3D c1 = map_sptr->get_coordinate_for_index(det_pos_pair.pos1()); + const stir::CartesianCoordinate3D c2 = map_sptr->get_coordinate_for_index(det_pos_pair.pos2()); + + // std::cout << "CListEventPETSIRD::get_bin(): det_pos1: " << det_pos_pair.pos1().tangential_coord() << ", " + // << det_pos_pair.pos1().axial_coord() << ", " << det_pos_pair.pos1().radial_coord() << std::endl; + // std::cout << "CListEventPETSIRD::get_bin(): det_pos2: " << det_pos_pair.pos2().tangential_coord() << ", " + // << det_pos_pair.pos2().axial_coord() << ", " << det_pos_pair.pos2().radial_coord() << std::endl; + // std::cout << "CListEventPETSIRD::get_bin(): c1: " << c1.x() << ", " << c1.y() << ", " << c1.z() << std::endl; + // std::cout << "CListEventPETSIRD::get_bin(): c2: " << c2.x() << ", " << c2.y() << ", " << c2.z() << std::endl; + const LORAs2Points lor(c1, c2); + bin = proj_data_info.get_bin(lor); + } + + } + + } diff --git a/src/listmode_buildblock/CListModeDataPETSIRD.cxx b/src/listmode_buildblock/CListModeDataPETSIRD.cxx index bbb00d73f6..fcad9674b4 100644 --- a/src/listmode_buildblock/CListModeDataPETSIRD.cxx +++ b/src/listmode_buildblock/CListModeDataPETSIRD.cxx @@ -270,6 +270,78 @@ CListModeDataPETSIRD::figure_out_block_angles(std::set& unique_angle_modu } } + +bool almostEqual(double a, double b, double tol = 1e-6) { + return std::fabs(a - b) <= tol; +} + +// Detect groupSize along dim2 (y) and loops along dim3 (z) +// Returns true on success, false if pattern doesn't match the assumed structure. +bool inferGroupSizes_dim2_dim3( + const std::vector>& pts, + std::size_t& groupSize_dim2, + std::size_t& groupSize_dim3, + float tol = 1e-5f +) { + const std::size_t n = pts.size(); + groupSize_dim2 = groupSize_dim3 = 0; + + if (n == 0) + return false; + + if (n == 1) { + groupSize_dim2 = 1; + groupSize_dim3 = 1; + return true; + } + + // STIR CartesianCoordinate3D is (z, y, x) + const float x0 = pts[0].x(); + const float z0 = pts[0].z(); + + // 1) Find how many initial points keep x and z the same + std::size_t runLen = 1; + while (runLen < n && + almostEqual(pts[runLen].x(), x0, tol) && + almostEqual(pts[runLen].z(), z0, tol)) { + ++runLen; + } + + groupSize_dim2 = runLen; + + // Must tile the full array + if (groupSize_dim2 == 0 || n % groupSize_dim2 != 0) + return false; + + groupSize_dim3 = n / groupSize_dim2; + + // 2) Check each block of size groupSize_dim2 has constant x,z + for (std::size_t b = 0; b < groupSize_dim3; ++b) { + std::size_t start = b * groupSize_dim2; + float xb = pts[start].x(); + float zb = pts[start].z(); + + for (std::size_t i = 1; i < groupSize_dim2; ++i) { + const auto& p = pts[start + i]; + if (!almostEqual(p.x(), xb, tol) || + !almostEqual(p.z(), zb, tol)) { + return false; // pattern breaks inside a block + } + } + } + + // 3) Optionally check that z actually changes between blocks + for (std::size_t b = 1; b < groupSize_dim3; ++b) { + float z_prev = pts[(b - 1) * groupSize_dim2].z(); + float z_curr = pts[b * groupSize_dim2].z(); + if (almostEqual(z_prev, z_curr, tol)) { + return false; // outer loop didn't move in z + } + } + + return true; +} + bool CListModeDataPETSIRD::isCylindricalConfiguration(const std::vector& replicated_module_list) { @@ -329,6 +401,47 @@ CListModeDataPETSIRD::isCylindricalConfiguration(const std::vector> pet_sird_positions; + const petsird::TypeOfModule type_of_module = replicated_module_list.size() - 1; + for (uint32_t module = 0; module < 1; module++) + { + for (uint32_t elem = 0; elem < numberOfElementsIndices; elem++) + { + petsird::ExpandedDetectionBin expanded_detection_bin{ module, elem, 1 }; + auto box_shape = petsird_helpers::geometry::get_detecting_box(*scanner_info, type_of_module, expanded_detection_bin); + CartesianCoordinate3D mean_coord; + for (auto& corner : box_shape.corners) + { // if STIR (z,y,x) -> PETSIRD (-y, -x, z) pheraps the order below needs to be changed + mean_coord.x() = +corner.c[0] / box_shape.corners.size(); + mean_coord.y() = +corner.c[1] / box_shape.corners.size(); + mean_coord.z() = +corner.c[2] / box_shape.corners.size(); + } + // mean_coord.z() += this_scanner_sptr->get_axial_crystal_spacing() / + // save mean pos into map + pet_sird_positions.push_back(mean_coord); + } + } + + std::cout << "Nikos here" << std::endl; + +if (inferGroupSizes_dim2_dim3(pet_sird_positions, group2, group3)) { + std::cout << "groupSize_dim2 = " << group2 << "\n"; + + std::cout << "groupSize_dim3 = " << group3 << "\n"; + +} else { + std::cout << "No (dim2, dim3) loop structure detected.\n"; + group2 = 1; + group3 = 1; +} + + std::cerr << "Nikos here" << std::endl; + + + } + std::vector element_horizontal_spacing, element_vertical_spacing; std::set unique_elements_horizontal_values, unique_elements_vertical_values; if (radius_indx == 0) @@ -345,6 +458,13 @@ CListModeDataPETSIRD::isCylindricalConfiguration(const std::vector 1 ? unique_elements_vertical_values.size()/group2 : group2; + std::cout << "blocks per bucket in transaxial direction = " << blocks_per_bucket_transaxial << "\n"; + blocks_per_bucket_axial = group3 > 1 ? unique_elements_horizontal_values.size()/(numberOfElementsIndices/group3) : group3; + std::cout << "blocks per bucket in axial direction = " << blocks_per_bucket_axial << "\n"; + num_axial_crystals_per_block = unique_elements_horizontal_values.size() / blocks_per_bucket_axial; + num_trans_crystals_per_block = unique_elements_vertical_values.size() / blocks_per_bucket_transaxial; + std::vector block_axial_spacing; get_spacing_uniform(block_axial_spacing, main_axis); if (block_axial_spacing.size() < 1) @@ -363,7 +483,7 @@ CListModeDataPETSIRD::isCylindricalConfiguration(const std::vectorenergy_resolution_at_511.front(), // energy_resolution_v - 511, // reference_energy_v - tof_bin_edges.NumberOfBins() + 1, - (tof_bin_edges.edges[1] - tof_bin_edges.edges[0])*10/2, - *unique_tof_values.begin() * 10 // non-TOF + 511 // reference_energy_v + // tof_bin_edges.NumberOfBins(), + // (tof_bin_edges.edges[1] - tof_bin_edges.edges[0])*10/2, + // *unique_tof_values.begin() * 10 // non-TOF )); // 13, @@ -442,17 +562,17 @@ CListModeDataPETSIRD::isCylindricalConfiguration(const std::vectorenergy_resolution_at_511.front(), // energy_resolution_v @@ -484,6 +604,8 @@ CListModeDataPETSIRD::CListModeDataPETSIRD(const std::string& listmode_filename, else current_lm_data_ptr.reset(new petsird::binary::PETSIRDReader(listmode_filename)); + m_has_delayeds = false; + current_lm_data_ptr->ReadHeader(header); scanner_info = std::make_shared(header.scanner); petsird::ScannerGeometry scanner_geo = scanner_info->scanner_geometry; @@ -518,48 +640,129 @@ CListModeDataPETSIRD::CListModeDataPETSIRD(const std::string& listmode_filename, // this_scanner_sptr.get_ DetectorCoordinateMap::det_pos_to_coord_type petsird_map; const petsird::TypeOfModule type_of_module = replicated_module_list.size() - 1; - // const auto event_energy_bin_edges=scanner_info.event_energy_bin_edges[type_of_module]; - // const auto num_event_energy_bins = event_energy_bin_edges.NumberOfBins(); - // error("TODO:GenericScanner"); + + petsird_to_stir = std::make_shared(); + + enum class InnerLoopDim { Axial, Tangential, Radial }; + InnerLoopDim inner_dim = InnerLoopDim::Tangential; // determined from your groupSize analysis + + // PRECOMPUTED from previous step: + std::size_t groupSize = blocks_per_bucket_transaxial == 1 ? 0 : num_trans_crystals_per_block; // e.g. 5, or 1 if purely monotonic + // extern InnerLoopDim inner_dim; // Axial / Tangential / Radial + + const int num_ax = blocks_per_bucket_axial * num_axial_crystals_per_block; + const int num_tang = blocks_per_bucket_transaxial * num_trans_crystals_per_block; + + int ind = 0; + for (uint32_t module = 0; module < numberOfModules; module++) for (uint32_t elem = 0; elem < numberOfElementsIndices; elem++) // for (uint32_t ener = 0; ener < num_event_energy_bins; ener++) //energy not supported yet { - int index = module * numberOfElementsIndices + elem; - // Here we are going to assume that the index = module*numberOfElementsIndices+elem is equal to ax+ - // num_ax*tang+ num_ax*num_tang*rad then we will pass this to the map sorter (set_detector_map)) - // therefore we can get ax, tang and rad from index - /* - * rad = index/(num_ax*num_tang) - * tang =(index-rad*num_ax*num_tang)/num_ax -num_tang/2 - * ax = index mod num_ax - */ - - int rad_pos = index / (this_scanner_sptr->get_num_rings() * this_scanner_sptr->get_num_detectors_per_ring()); - int ax_pos = (index - rad_pos * this_scanner_sptr->get_num_rings() * this_scanner_sptr->get_num_detectors_per_ring()) - / this_scanner_sptr->get_num_detectors_per_ring(); - int tang_pos = index % this_scanner_sptr->get_num_detectors_per_ring(); - - DetectionPosition<> detpos(tang_pos, ax_pos, rad_pos); - petsird::ExpandedDetectionBin expanded_detection_bin{ module, elem, 1 }; + // ---- 1) Decompose elem into: tile, in-tile indices ---- + const uint32_t tileSize = groupSize * groupSize; // elems per tile + const uint32_t tiles_per_bucket = + blocks_per_bucket_axial * blocks_per_bucket_transaxial; - auto box_shape = petsird_helpers::geometry::get_detecting_box(*scanner_info, type_of_module, expanded_detection_bin); - CartesianCoordinate3D mean_coord; + const uint32_t tile = (groupSize > 0 ? elem / tileSize : 0); // which tile + const uint32_t inTile = (groupSize > 0 ? elem % tileSize : elem); // index inside tile - for (auto& corner : box_shape.corners) - { // if STIR (z,y,x) -> PETSIRD (-y, -x, z) pheraps the order below needs to be changed - mean_coord.x() = +corner.c[0] / box_shape.corners.size() * 10 ; - mean_coord.y() = +corner.c[1] / box_shape.corners.size() * 10 ; - mean_coord.z() = +corner.c[2] / box_shape.corners.size(); - } + const uint32_t i0 = inTile % groupSize; // fast inside tile (local "x") + const uint32_t i1 = inTile / groupSize; // slow inside tile (local "y") - // save mean pos into map - petsird_map[detpos] = mean_coord; - std::cout << "NIKOSSS " << detpos.radial_coord() << ", " << detpos.axial_coord() << ", " << detpos.tangential_coord() << ", " - << mean_coord.x() << ", " << mean_coord.y() << ", " << mean_coord.z() << ", " << std::endl; + int ax_pos = 0; + int tang_pos = 0; + int rad_pos = 0; // ignored for now + + // ---- 2) Decode which block (tile) we are in along axial/tangential ---- + switch (inner_dim) + { + case InnerLoopDim::Tangential: + { + // Here we assume: + // - i0 runs tangential inside a block + // - i1 runs axial inside a block + // + // tiles are laid out as: + // tangential: blocks_per_bucket_transaxial tiles + // axial: blocks_per_bucket_axial tiles + + const uint32_t tang_block = tile % blocks_per_bucket_transaxial; + const uint32_t axial_block = tile / blocks_per_bucket_transaxial; + + tang_pos = static_cast(tang_block * groupSize + i0); + ax_pos = static_cast(axial_block * groupSize + i1); + break; + } + + case InnerLoopDim::Axial: + { + // Here we assume: + // - i0 runs axial inside a block + // - i1 runs tangential inside a block + // + // tiles are laid out as: + // axial: blocks_per_bucket_axial tiles + // tangential: blocks_per_bucket_transaxial tiles + + const uint32_t axial_block = tile % blocks_per_bucket_axial; + const uint32_t tang_block = tile / blocks_per_bucket_axial; + + ax_pos = static_cast(axial_block * groupSize + i0); + tang_pos = static_cast(tang_block * groupSize + i1); + break; + } + } + + DetectionPosition<> detpos(tang_pos + module * (num_trans_crystals_per_block * blocks_per_bucket_transaxial), + ax_pos, rad_pos); + + petsird::ExpandedDetectionBin expanded_detection_bin{ module, elem, 0 }; + + auto box_shape = + petsird_helpers::geometry::get_detecting_box( + *scanner_info, type_of_module, expanded_detection_bin); + + CartesianCoordinate3D mean_coord(0.f, 0.f, 0.f); + + for (auto& corner : box_shape.corners) + { + mean_coord.x() += corner.c[0] / box_shape.corners.size(); + mean_coord.y() += corner.c[1] / box_shape.corners.size(); + mean_coord.z() += corner.c[2] / box_shape.corners.size(); + } + // mean_coord.z() = (round(mean_coord.z() * 1000.0F)) / 1000.0F; + // mean_coord.y() = (round(mean_coord.y() * 1000.0F)) / 1000.0F; + // mean_coord.x() = (round(mean_coord.x() * 1000.0F)) / 1000.0F; + + petsird_map[detpos] = mean_coord; + + // auto detectionBin = petsird_helpers::make_detection_bin( + // *scanner_info, + // type_of_module, + // expanded_detection_bin); + + // petsird_map[detectionBin] = mean_coord; + + // Save to shared_ptr map + (*petsird_to_stir)[expanded_detection_bin] = detpos; + + std::cout << ind << " : " + << detpos.radial_coord() << ", " + << detpos.axial_coord() << ", " + << detpos.tangential_coord() << ", " + << mean_coord.x() << ", " + << mean_coord.y() << ", " + << mean_coord.z() << "\n"; + ++ind; } + // this->map.reset(new DetectorCoordinateMapLightPETSIRD(petsird_map)); this->map.reset(new DetectorCoordinateMap(petsird_map)); + // this_scanner_sptr->get_detector_map_sptr()->set_detector_coordinate_map_light_sptr( + // std::make_shared(petsird_map)); + + this_scanner_sptr->set_detector_map(petsird_map); int tof_mash_factor = 1; this->set_proj_data_info_sptr(std::const_pointer_cast( @@ -604,7 +807,7 @@ CListModeDataPETSIRD::open_lm_file() const shared_ptr CListModeDataPETSIRD::get_empty_record_sptr() const { - shared_ptr sptr(new CListRecordPETSIRD(scanner_info, this_scanner_sptr)); + shared_ptr sptr(new CListRecordPETSIRD(scanner_info, this_scanner_sptr, this->map, petsird_to_stir)); std::dynamic_pointer_cast(sptr)->event().set_scanner_sptr( this->get_proj_data_info_sptr()->get_scanner_sptr()); std::dynamic_pointer_cast(sptr)->event().set_PETSIRD_ranges(numberOfModules, numberOfElementsIndices); diff --git a/src/recon_buildblock/find_basic_vs_nums_in_subset.cxx b/src/recon_buildblock/find_basic_vs_nums_in_subset.cxx index 5364d4fc9b..240059b56f 100644 --- a/src/recon_buildblock/find_basic_vs_nums_in_subset.cxx +++ b/src/recon_buildblock/find_basic_vs_nums_in_subset.cxx @@ -39,12 +39,14 @@ find_basic_vs_nums_in_subset(const ProjDataInfo& proj_data_info, 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(); + 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) { + // std::cout << "view: " << view << std::endl; + // std::cout << "timing_pos_num: " << timing_pos_num << std::endl; const ViewSegmentNumbers view_segment_num(view, segment_num); if (!symmetries.is_basic(view_segment_num)) From 4e954236ad22d54c9364bedb243be24924db02dc Mon Sep 17 00:00:00 2001 From: NikEfth Date: Mon, 15 Dec 2025 17:59:11 +0100 Subject: [PATCH 39/42] Working: 1. Cylindrical geometry with TOF support 2. Blocks on cylindrical geometry --- src/IO/PETSIRDCListmodeInputFileFormat.cxx | 31 + .../stir/IO/PETSIRDCListmodeInputFileFormat.h | 3 +- .../stir/ProjDataInfoCylindricalNoArcCorr.inl | 2 +- .../stir/listmode/CListModeDataPETSIRD.h | 118 ++- .../stir/listmode/CListRecordPETSIRD.h | 64 +- .../stir/listmode/CListRecordPETSIRD.inl | 110 +-- .../CListModeDataPETSIRD.cxx | 767 ++++++++++-------- 7 files changed, 659 insertions(+), 436 deletions(-) diff --git a/src/IO/PETSIRDCListmodeInputFileFormat.cxx b/src/IO/PETSIRDCListmodeInputFileFormat.cxx index e941420dc2..cd187be14f 100644 --- a/src/IO/PETSIRDCListmodeInputFileFormat.cxx +++ b/src/IO/PETSIRDCListmodeInputFileFormat.cxx @@ -1,3 +1,34 @@ +/* PETSIRDCListmodeInputFileFormat.h + + Class defining input file format for coincidence listmode data for PETSIRD. + + Copyright 2025, UMCG + Copyright 2025 National Physical Laboratory + + 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 + + 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. + */ + +/*! + + \file + \ingroup listmode + \brief Declaration of class stir::PETSIRDCListmodeInputFileFormat + + \author Nikos Efthimiou + \author Daniel Deidda + +*/ + #include "stir/IO/PETSIRDCListmodeInputFileFormat.h" #include "petsird/binary/protocols.h" #include "petsird/hdf5/protocols.h" diff --git a/src/include/stir/IO/PETSIRDCListmodeInputFileFormat.h b/src/include/stir/IO/PETSIRDCListmodeInputFileFormat.h index 9c9f2bb07e..82840129d6 100644 --- a/src/include/stir/IO/PETSIRDCListmodeInputFileFormat.h +++ b/src/include/stir/IO/PETSIRDCListmodeInputFileFormat.h @@ -25,8 +25,7 @@ \ingroup listmode \brief Declaration of class stir::PETSIRDCListmodeInputFileFormat - \author Jannis Fischer - \author Markus Jehl, Positrigo + \author Nikos Efthimiou \author Daniel Deidda */ diff --git a/src/include/stir/ProjDataInfoCylindricalNoArcCorr.inl b/src/include/stir/ProjDataInfoCylindricalNoArcCorr.inl index 9f3678de3f..ff37ac0ee2 100644 --- a/src/include/stir/ProjDataInfoCylindricalNoArcCorr.inl +++ b/src/include/stir/ProjDataInfoCylindricalNoArcCorr.inl @@ -124,7 +124,7 @@ ProjDataInfoCylindricalNoArcCorr::get_bin_for_det_pair( } else { - bin.timing_pos_num() = -timing_pos_num; + bin.timing_pos_num() = -timing_pos_num - (get_num_tof_poss() % 2 == 0); return get_segment_axial_pos_num_for_ring_pair(bin.segment_num(), bin.axial_pos_num(), ring_num2, ring_num1); } } diff --git a/src/include/stir/listmode/CListModeDataPETSIRD.h b/src/include/stir/listmode/CListModeDataPETSIRD.h index b7d7268105..181278855f 100644 --- a/src/include/stir/listmode/CListModeDataPETSIRD.h +++ b/src/include/stir/listmode/CListModeDataPETSIRD.h @@ -2,6 +2,7 @@ Coincidence LM Data Class for PETSIRD + Copyright 2025, UMCG Copyright 2025, MGH / HST A. Martinos Center for Biomedical Imaging Licensed under the Apache License, Version 2.0 (the "License"); @@ -42,13 +43,11 @@ Coincidence LM Data Class for PETSIRD START_NAMESPACE_STIR /*! - \brief Class for reading PETSIRD listmode data with variable geometry - \ingroup listmode - \par - By providing crystal map and template projection data files, the coordinates are read from files and used defining the LOR - coordinates. -*/ + \brief Comparator for ordering petsird::ExpandedDetectionBin in std::map. + \details + Orders by module_index, then element_index, then energy_index. +*/ struct ExpandedDetectionBinLess { @@ -65,15 +64,60 @@ struct ExpandedDetectionBinLess } }; +/*! + \brief Mapping type from PETSIRD ExpandedDetectionBin to STIR DetectionPosition. +*/ using PETSIRDToSTIRMap = std::map< petsird::ExpandedDetectionBin, stir::DetectionPosition<>, ExpandedDetectionBinLess >; +/*! + \class CListModeDataPETSIRD + \brief Reader for PETSIRD listmode data supporting variable geometry. + \ingroup listmode + + \par Overview + - Supports HDF5 and binary PETSIRD formats. + - Infers scanner geometry: + - Cylindrical → creates cylindrical scanner. + - Block-based → creates block-based scanner. + - Otherwise → creates generic scanner using crystal positions. + - Builds a DetectorCoordinateMap when needed and stores to disk. + \par + + Infering the scanner geometry makes a lot of assumptions about what PET is. + In particular, it assumes: + \li A PET scanner is made of rings of detectors + \li The largest axis is the axial one. + \li So far we support only a single layer. This is partly hard-coded for simplicity. (look in the code for relevant TODOs and comments.) + \li Some of the hardcoded assumptions are in CListRecordPETSIRD as well. + + \note Exact PETSIRD format specification is defined in the PETSIRD project documentation. (NOTE: It looks like GATE.) + \note Initially, I wanted to: + - Is close to a cylindrical geometry ? + - then yes use a cylindrical scanner that is simpler. + - Else, is it made of blocks arranged on a cylinder. + + However, now I do the following: + - Is close to cylindriacl geometry? + - yes use cylindrical scanner + - Check if blocks-on-cylinder configuration, are a good match. + - yes use blocks-on-cylinder scanner + - else use generic scanner and export the map to the disk. + + If listmode reconstruciton is done, the map is regenerated on-the-fly. + +*/ class CListModeDataPETSIRD : public CListModeDataBasedOnCoordinateMap { public: + /*! + \brief Construct reader. + \param listmode_filename Path to PETSIRD listmode file. + \param use_hdf5 If true, use HDF5 reader; otherwise use binary reader. + */ CListModeDataPETSIRD(const std::string& listmode_filename, bool use_hdf5); virtual shared_ptr get_empty_record_sptr() const override; @@ -94,48 +138,69 @@ class CListModeDataPETSIRD : public CListModeDataBasedOnCoordinateMap mutable shared_ptr current_lm_data_ptr; private: + //! Whether to use the HDF5 reader. const bool use_hdf5; mutable unsigned long int curr_event_in_event_block = 0; mutable petsird::TimeBlock curr_time_block; + //! Number of replicated modules. int numberOfModules; - + //! Number of element indices per module. int numberOfElementsIndices; - + //! Transaxial blocks per bucket (scanner metadata). int blocks_per_bucket_transaxial; - + //! Axial blocks per bucket (scanner metadata). int blocks_per_bucket_axial; - + //! Number of axial crystals per block. int num_axial_crystals_per_block; - + //! Number of transaxial crystals per block. int num_trans_crystals_per_block; mutable petsird::EventTimeBlock curr_event_block; - + //! Mapping from PETSIRD expanded bins to STIR detection positions. shared_ptr petsird_to_stir; - + //! Active module pair (prompt/delayed, or two modules for coincidences). + //! \todo: This hard-codes a single/matterial layer detector assumption. petsird::TypeOfModulePair type_of_module_pair{ 0, 0 }; - + //! Active scanner instance. shared_ptr this_scanner_sptr; - + //! Scanner information as provided by PETSIRD. shared_ptr scanner_info; - + //! Current event prompt flag. mutable bool curr_is_prompt = true; - + //! Whether delayed events are present. mutable bool m_has_delayeds; - + /*! + \brief Detect if the PETSIRD geometry is cylindrical and initialise scanner/map accordingly. + \param replicated_module_list PETSIRD replicated detector modules. + \return True if cylindrical configuration was detected. + */ bool isCylindricalConfiguration(const std::vector& replicated_module_list); - - void find_uniqe_values_1D(std::set& values, const std::vector& input); - - void find_uniqe_values_2D(std::set& values, const std::vector>& input); - + + /*! + \brief Infer scanner blocks and rotation axis from PETSIRD replicated modules. + \param unique_dim1_values Output set of unique translations along X. + \param unique_dim2_values Output set of unique translations along Y. + \param unique_dim3_values Output set of unique translations along Z. + \param replicated_module_list PETSIRD replicated detector modules. + \return Index of rotation axis (0=x, 1=y, 2=z) or -1 if not found. + */ int figure_out_scanner_blocks_and_rotation_axis(std::set& unique_dim1_values, std::set& unique_dim2_values, std::set& unique_dim3_values, const std::vector& replicated_module_list); + /*! + \brief Determine block element translations and radius. + \param unique_dim1_values Output set of element translations along X. + \param unique_dim2_values Output set of element translations along Y. + \param unique_dim3_values Output set of element translations along Z. + \param radius Output inferred radius. + \param radius_index Output axis index containing the radius component. + \param rotation_axis Known rotation axis (0=x, 1=y, 2=z). + \param replicated_module_list PETSIRD replicated detector modules. + */ void figure_out_block_element_transformations(std::set& unique_dim1_values, std::set& unique_dim2_values, std::set& unique_dim3_values, @@ -143,7 +208,12 @@ class CListModeDataPETSIRD : public CListModeDataBasedOnCoordinateMap int& radius_index, const int rotation_axis, const std::vector& replicated_module_list); - + /*! + \brief Compute unique module rotation angles around the given axis. + \param unique_angle_modules Output set of unique angles (radians). + \param rot_axis Rotation axis index (0=x, 1=y, 2=z). + \param replicated_module_list PETSIRD replicated detector modules. + */ void figure_out_block_angles(std::set& unique_angle_modules, const int rot_axis, const std::vector& replicated_module_list); diff --git a/src/include/stir/listmode/CListRecordPETSIRD.h b/src/include/stir/listmode/CListRecordPETSIRD.h index 038cc9b3a9..fb8d319aaa 100644 --- a/src/include/stir/listmode/CListRecordPETSIRD.h +++ b/src/include/stir/listmode/CListRecordPETSIRD.h @@ -2,9 +2,7 @@ Coincidence Event Class for PETSIRD: Header File - Copyright 2015 ETH Zurich, Institute of Particle Physics - Copyright 2017 ETH Zurich, Institute of Particle Physics and Astrophysics - Copyright 2020, 2022 Positrigo AG, Zurich + Copyright 2025, UMCG Copyright 2025 National Physical Laboratory Licensed under the Apache License, Version 2.0 (the "License"); @@ -27,9 +25,7 @@ Coincidence Event Class for PETSIRD: Header File \ingroup listmode \brief Declaration of class stir::CListEventPETSIRD and stir::CListRecordPETSIRD with supporting classes -\author Jannis Fischer -\author Parisa Khateri -\author Markus Jehl +\author Nikos Efthimiou \author Daniel Deidda */ @@ -68,32 +64,30 @@ class CListEventPETSIRD : public CListEvent //! Override the default implementation inline void get_bin(Bin& bin, const ProjDataInfo& proj_data_info) const override; - inline void set_map_sptr(shared_ptr new_map_sptr) { map_sptr = new_map_sptr; } - /*! Set the scanner */ - /*! Currently only used if the map is not set. */ + inline void set_map_sptr(shared_ptr new_map_sptr) { map_sptr = new_map_sptr;} + + inline void set_petsird_to_stir_map(shared_ptr new_map) { petsird_to_stir = new_map; } + inline void set_scanner_sptr(shared_ptr new_scanner_sptr) { scanner_sptr = new_scanner_sptr; } - virtual bool is_valid_template(const ProjDataInfo&) const override { return true; } + inline bool is_valid_template(const ProjDataInfo&) const override { return true; } - virtual bool is_prompt() const override { return _prompt; } + inline bool is_prompt() const override { return _prompt; } - virtual Succeeded set_prompt(const bool prompt) override + inline Succeeded set_prompt(const bool prompt) override { _prompt = prompt; return Succeeded::yes; } - void set_PETSIRD_ranges(int _numberOfModules, int _numberOfElementsIndices) + inline void set_expanded_detection_bins(const petsird_helpers::ExpandedDetectionBin& det0, + const petsird_helpers::ExpandedDetectionBin& det1) { - numberOfModules = _numberOfModules; - numberOfElementsIndices = _numberOfElementsIndices; - } - - int numberOfModules; + exp_det_0 = det0; + exp_det_1 = det1; + } - int numberOfElementsIndices; - - petsird_helpers::ExpandedDetectionBin exp_det_0, exp_det_1; + inline void set_tof_bin(const uint32_t value) { tof_bin = value; } inline stir::DetectionPosition<> get_stir_det_pos_from_PETSIRD_id(const petsird_helpers::ExpandedDetectionBin& exp_det_bin) const; @@ -103,8 +97,8 @@ class CListEventPETSIRD : public CListEvent shared_ptr map_sptr; shared_ptr scanner_sptr; bool _prompt; - - const DetectorCoordinateMap& map_to_use() const { return map_sptr ? *map_sptr : *this->scanner_sptr->get_detector_map_sptr(); } + petsird_helpers::ExpandedDetectionBin exp_det_0, exp_det_1; + uint32_t tof_bin; }; class CListTimePETSIRD : public ListTime @@ -124,15 +118,10 @@ class CListRecordPETSIRD : public CListRecord { public: CListRecordPETSIRD(shared_ptr scanner_info, - shared_ptr scanner_sptr, - // shared_ptr map_sptr, - shared_ptr map_sptr, - shared_ptr petsird_to_stir) + shared_ptr scanner_sptr) : scanner_info(scanner_info) { event_data.set_scanner_sptr(scanner_sptr); - event_data.set_map_sptr(map_sptr); - event_data.petsird_to_stir = petsird_to_stir; } // ~CListRecordPETSIRD() override {} @@ -154,15 +143,18 @@ class CListRecordPETSIRD : public CListRecord virtual Succeeded init_from_data(const petsird::CoincidenceEvent& data, bool is_prompt = true) { - event_data.exp_det_0 = petsird_helpers::expand_detection_bin(*scanner_info, - 0, - data.detection_bins[0]); - event_data.exp_det_1 = petsird_helpers::expand_detection_bin(*scanner_info, - 0, - data.detection_bins[1]); + event_data.set_expanded_detection_bins( + petsird_helpers::expand_detection_bin(*scanner_info, + 0, // TODO type_of_module, currently we only support single module types. + data.detection_bins[0]), + petsird_helpers::expand_detection_bin(*scanner_info, + 0, // TODO type_of_module, currently we only support single module types. + data.detection_bins[1]) + ); - event_data.set_prompt(is_prompt); + event_data.set_prompt(is_prompt); + event_data.set_tof_bin(data.tof_idx); return Succeeded::yes; } diff --git a/src/include/stir/listmode/CListRecordPETSIRD.inl b/src/include/stir/listmode/CListRecordPETSIRD.inl index d739486188..8059907a35 100644 --- a/src/include/stir/listmode/CListRecordPETSIRD.inl +++ b/src/include/stir/listmode/CListRecordPETSIRD.inl @@ -1,11 +1,7 @@ /* CListRecordPETSIRD.inl + Coincidence Event Class for PETSIRD: Inline File - Coincidence Event Class for PETSIRD: Inline File - - Copyright 2015 ETH Zurich, Institute of Particle Physics - Copyright 2017 ETH Zurich, Institute of Particle Physics and Astrophysics - Copyright 2020, 2022 Positrigo AG, Zurich - Copyright 2021 University College London + Copyright 2025, UMCG Copyright 2025 National Physical Laboratory Licensed under the Apache License, Version 2.0 (the "License"); @@ -43,18 +39,22 @@ START_NAMESPACE_STIR -stir::DetectionPosition<> +stir::DetectionPosition<> CListEventPETSIRD::get_stir_det_pos_from_PETSIRD_id(const petsird::ExpandedDetectionBin& exp_det_bin) const { -// const-friendly lookup + // const-friendly lookup auto it = petsird_to_stir->find(exp_det_bin); - if (it == petsird_to_stir->end()) { - // handle missing key however STIR usually does: - // - throw - // - or call error(...) - // - or return a default DetectionPosition - error("get_stir_det_pos_from_PETSIRD_id: PETSIRD id not found in petsird_to_stir map", exp_det_bin.module_index, exp_det_bin.element_index, exp_det_bin.energy_index); - } + if (it == petsird_to_stir->end()) + { + // handle missing key however STIR usually does: + // - throw + // - or call error(...) + // - or return a default DetectionPosition + error("get_stir_det_pos_from_PETSIRD_id: PETSIRD id not found in petsird_to_stir map", + exp_det_bin.module_index, + exp_det_bin.element_index, + exp_det_bin.energy_index); + } return it->second; // copy of DetectionPosition<> } @@ -65,12 +65,12 @@ CListEventPETSIRD::get_LOR() const LORAs2Points lor; DetectionPositionPair<> det_pos_pair; - det_pos_pair.pos1() = get_stir_det_pos_from_PETSIRD_id(exp_det_0); - det_pos_pair.pos2() = get_stir_det_pos_from_PETSIRD_id(exp_det_1); + det_pos_pair.pos1() = get_stir_det_pos_from_PETSIRD_id(exp_det_0); + det_pos_pair.pos2() = get_stir_det_pos_from_PETSIRD_id(exp_det_1); + + // lor.p1() = map_to_use().get_coordinate_for_index(det_pos_pair.pos1()); + // lor.p2() = map_to_use().get_coordinate_for_index(det_pos_pair.pos2()); - lor.p1() = map_to_use().get_coordinate_for_index(det_pos_pair.pos1()); - lor.p2() = map_to_use().get_coordinate_for_index(det_pos_pair.pos2()); - // std::cout << "lor_p1 " << lor.p1().x() << " " << lor.p1().y() << " " << lor.p1().z() << std::endl; // std::cout << "lor_p2 " < det_pos_pair; - if(scanner_sptr->get_scanner_geometry() == "Cylindrical") + if (scanner_sptr->get_scanner_geometry() == "Cylindrical") { - det_pos_pair.pos1() = get_stir_det_pos_from_PETSIRD_id(exp_det_0); - det_pos_pair.pos2() = get_stir_det_pos_from_PETSIRD_id(exp_det_1); - // this->get_data().get_detection_position_pair(det_pos_pair); + det_pos_pair.pos1() = get_stir_det_pos_from_PETSIRD_id(exp_det_1); + det_pos_pair.pos2() = get_stir_det_pos_from_PETSIRD_id(exp_det_0); + det_pos_pair.timing_pos() = static_cast(tof_bin) + (proj_data_info.get_min_tof_pos_num() ); dynamic_cast(proj_data_info).get_bin_for_det_pos_pair(bin, det_pos_pair); } - else + else if (scanner_sptr->get_scanner_geometry() == "Generic") { - if (!map_sptr) - { - std::cerr << "Error: No detector map set in CListEventPETSIRD::get_bin()" << std::endl; - // this->get_data().get_detection_position_pair(det_pos_pair); - } - else{ - DetectionPositionPair<> det_pos_pair; - det_pos_pair.pos1() = get_stir_det_pos_from_PETSIRD_id(exp_det_0); - det_pos_pair.pos2() = get_stir_det_pos_from_PETSIRD_id(exp_det_1); - // std::cout<< exp_det_0.module_index << ", " << exp_det_0.element_index << " ---- " << exp_det_1.module_index << ", " << exp_det_1.element_index << std::endl; - const stir::CartesianCoordinate3D c1 = map_sptr->get_coordinate_for_index(det_pos_pair.pos1()); - const stir::CartesianCoordinate3D c2 = map_sptr->get_coordinate_for_index(det_pos_pair.pos2()); - - // std::cout << "CListEventPETSIRD::get_bin(): det_pos1: " << det_pos_pair.pos1().tangential_coord() << ", " - // << det_pos_pair.pos1().axial_coord() << ", " << det_pos_pair.pos1().radial_coord() << std::endl; - // std::cout << "CListEventPETSIRD::get_bin(): det_pos2: " << det_pos_pair.pos2().tangential_coord() << ", " - // << det_pos_pair.pos2().axial_coord() << ", " << det_pos_pair.pos2().radial_coord() << std::endl; - // std::cout << "CListEventPETSIRD::get_bin(): c1: " << c1.x() << ", " << c1.y() << ", " << c1.z() << std::endl; - // std::cout << "CListEventPETSIRD::get_bin(): c2: " << c2.x() << ", " << c2.y() << ", " << c2.z() << std::endl; - const LORAs2Points lor(c1, c2); - bin = proj_data_info.get_bin(lor); - } - + // if (!map_sptr) + // { + // std::cerr << "Error: No detector map set in CListEventPETSIRD::get_bin()" << std::endl; + // // this->get_data().get_detection_position_pair(det_pos_pair); + // } + // else{ + // DetectionPositionPair<> det_pos_pair; + // det_pos_pair.pos1() = get_stir_det_pos_from_PETSIRD_id(exp_det_0); + // det_pos_pair.pos2() = get_stir_det_pos_from_PETSIRD_id(exp_det_1); + // // std::cout<< exp_det_0.module_index << ", " << exp_det_0.element_index << " ---- " << exp_det_1.module_index + // << ", " << exp_det_1.element_index << std::endl; const stir::CartesianCoordinate3D c1 = + // map_sptr->get_coordinate_for_index(det_pos_pair.pos1()); const stir::CartesianCoordinate3D c2 = + // map_sptr->get_coordinate_for_index(det_pos_pair.pos2()); + + // // std::cout << "CListEventPETSIRD::get_bin(): det_pos1: " << det_pos_pair.pos1().tangential_coord() << ", " + // // << det_pos_pair.pos1().axial_coord() << ", " << det_pos_pair.pos1().radial_coord() << std::endl; + // // std::cout << "CListEventPETSIRD::get_bin(): det_pos2: " << det_pos_pair.pos2().tangential_coord() << ", " + // // << det_pos_pair.pos2().axial_coord() << ", " << det_pos_pair.pos2().radial_coord() << std::endl; + // // std::cout << "CListEventPETSIRD::get_bin(): c1: " << c1.x() << ", " << c1.y() << ", " << c1.z() << + // std::endl; + // // std::cout << "CListEventPETSIRD::get_bin(): c2: " << c2.x() << ", " << c2.y() << ", " << c2.z() << + // std::endl; const LORAs2Points lor(c1, c2); bin = proj_data_info.get_bin(lor); + // } + } + else if (scanner_sptr->get_scanner_geometry() == "BlocksOnCylindrical") + { + det_pos_pair.pos1() = (*petsird_to_stir)[exp_det_0]; + det_pos_pair.pos2() = (*petsird_to_stir)[exp_det_1]; + dynamic_cast(proj_data_info).get_bin_for_det_pos_pair(bin, det_pos_pair); + } + else + { + error("CListEventPETSIRD::get_bin: How did I get with an unsupported scanner geometry ? -", + scanner_sptr->get_scanner_geometry()); } - - } - END_NAMESPACE_STIR diff --git a/src/listmode_buildblock/CListModeDataPETSIRD.cxx b/src/listmode_buildblock/CListModeDataPETSIRD.cxx index fcad9674b4..f118752d44 100644 --- a/src/listmode_buildblock/CListModeDataPETSIRD.cxx +++ b/src/listmode_buildblock/CListModeDataPETSIRD.cxx @@ -2,6 +2,7 @@ Coincidence LM Data Class for PETSIRD: Implementation + Copyright 2025, UMCG Copyright 2025, MGH / HST A. Martinos Center for Biomedical Imaging Licensed under the Apache License, Version 2.0 (the "License"); @@ -43,12 +44,37 @@ Coincidence LM Data Class for PETSIRD: Implementation START_NAMESPACE_STIR +/*! + \namespace matrix + \brief Lightweight 3x3 matrix and 3D vector helpers used during PETSIRD geometry analysis. + + Provides utilities to: + - transpose a 3x3 matrix, + - subtract two 3x3 matrices, + - extract a rotation axis vector from the skew-symmetric part of a matrix. + + \details + - Mat3: std::array,3> for compact fixed-size storage. + - Vec3: std::array for simple 3D vectors. + - getAxisFromSkew(): + Given S = R - R^T (skew-symmetric part of a rotation matrix R), + returns the axis proportional to: + (S_z,y - S_y,z)/2, (S_x,z - S_z,x)/2, (S_y,x - S_x,y)/2. + For a pure rotation, S encodes the axis direction. + - These helpers assume small numerical noise; thresholds are handled by callers. + - No external dependencies; intended for quick geometric inference (e.g., rotation axis detection). +*/ namespace matrix { using Mat3 = std::array, 3>; using Vec3 = std::array; +/*! + \brief Transpose a 3x3 matrix. + \param mat Input matrix. + \return Transposed matrix. +*/ inline Mat3 transpose(const Mat3& mat) { @@ -59,6 +85,12 @@ transpose(const Mat3& mat) return result; } +/*! + \brief Subtract two 3x3 matrices (A - B). + \param A Left-hand matrix. + \param B Right-hand matrix. + \return Result of A - B. +*/ inline Mat3 subtract(const Mat3& A, const Mat3& B) { @@ -69,6 +101,11 @@ subtract(const Mat3& A, const Mat3& B) return result; } +/*! + \brief Extract rotation axis from the skew-symmetric matrix S = R - R^T. + \param S Skew-symmetric matrix. + \return Axis vector proportional to the rotation axis. +*/ inline Vec3 getAxisFromSkew(const Mat3& S) { @@ -81,6 +118,28 @@ getAxisFromSkew(const Mat3& S) } // namespace matrix +/*! + \namespace vector_utils + \brief Helpers for spacing analysis and axis inference from coordinate sets. + + \details + - \ref vector_utils::get_spacing_uniform computes successive spacings between + sorted unique values and tests if they are uniform within a tolerance. + - \ref vector_utils::getLargestVector returns the largest of three coordinate + sets and logs which axis is inferred as “axial”. +*/ +namespace vector_utils +{ +/*! + \brief Compute spacings between sorted unique values and test uniformity. + \param spacing Output vector. Appends |x[i] - x[i-1]| for i=1..N-1, where x is the sorted version of \a unsorted_block_poss. + \param unsorted_block_poss Set of unique positions (e.g., angles or translations). + \param epsilon Tolerance for uniformity check (default 1e-4). + \return True if all spacings differ from the first spacing by <= \a epsilon, false otherwise. + + \details + - If \a spacing ends up empty (e.g., input size < 2), returns true (trivially uniform). +*/ inline bool get_spacing_uniform(std::vector& spacing, const std::set& unsorted_block_poss, double epsilon = 1e-4) { @@ -93,6 +152,16 @@ get_spacing_uniform(std::vector& spacing, const std::set& unsorted return std::all_of(spacing.begin(), spacing.end(), [&](float s) { return std::abs(s - spacing.front()) <= epsilon; }); } +/*! + \brief Return the largest of three sets and report inferred axial direction. + \param x Values along X. + \param y Values along Y. + \param z Values along Z. + \return Const reference to the largest set among \a x, \a y, \a z. + + \details + Logs the index of the inferred axial direction: 0 (x), 1 (y), or 2 (z). +*/ const std::set& getLargestVector(const std::set& x, const std::set& y, const std::set& z) { @@ -113,8 +182,13 @@ getLargestVector(const std::set& x, const std::set& y, const std:: return *largest; } +/*! + \brief Collect unique values from a 1D vector. + \param values Output set for unique values. + \param input Input vector. +*/ void -CListModeDataPETSIRD::find_uniqe_values_1D(std::set& values, const std::vector& input) +find_unique_values_1D(std::set& values, const std::vector& input) { for (float val : input) { @@ -123,14 +197,34 @@ CListModeDataPETSIRD::find_uniqe_values_1D(std::set& values, const std::v } } +/*! + \brief Collect unique values from a 2D vector (matrix). + \param values Output set for unique values. + \param input Input 2D vector [rows][cols]. +*/ void -CListModeDataPETSIRD::find_uniqe_values_2D(std::set& values, const std::vector>& input) +find_unique_values_2D(std::set& values, const std::vector>& input) { for (size_t row = 0; row < input.size(); ++row) for (size_t col = 0; col < input[row].size(); ++col) values.insert(input[row][col]); } +} // namespace vector_utils + +/*! + \brief Infer scanner blocks and rotation axis from PETSIRD replicated modules. + \param unique_dim1_values Output set of unique translations along X. + \param unique_dim2_values Output set of unique translations along Y. + \param unique_dim3_values Output set of unique translations along Z. + \param replicated_module_list PETSIRD replicated detector modules. + \return Index of rotation axis (0=x, 1=y, 2=z) or -1 if not found. + + \details + - Extracts translation components into the provided sets. + - Uses skew-symmetric part of rotation matrices to infer axis direction. + - Emits warnings for mixed-axis rotations or inconsistencies. +*/ int CListModeDataPETSIRD::figure_out_scanner_blocks_and_rotation_axis( std::set& unique_dim1_values, @@ -192,6 +286,20 @@ CListModeDataPETSIRD::figure_out_scanner_blocks_and_rotation_axis( return detected_axis; } +/*! + \brief Determine block element translations and radius. + \param unique_dim1_values Output set of element translations along X. + \param unique_dim2_values Output set of element translations along Y. + \param unique_dim3_values Output set of element translations along Z. + \param radius Output inferred radius (positive component orthogonal to rotation axis). + \param radius_index Output index of the axis containing the radius component. + \param rotation_axis Known rotation axis (0=x, 1=y, 2=z). + \param replicated_module_list PETSIRD replicated detector modules. + + \details + - Scans element-level transforms to infer radius and collect translations. + - Emits warnings if mixed radii are detected. +*/ void CListModeDataPETSIRD::figure_out_block_element_transformations( std::set& unique_dim1_values, @@ -270,76 +378,81 @@ CListModeDataPETSIRD::figure_out_block_angles(std::set& unique_angle_modu } } - -bool almostEqual(double a, double b, double tol = 1e-6) { - return std::fabs(a - b) <= tol; +bool +almostEqual(double a, double b, double tol = 1e-6) +{ + return std::fabs(a - b) <= tol; } // Detect groupSize along dim2 (y) and loops along dim3 (z) // Returns true on success, false if pattern doesn't match the assumed structure. -bool inferGroupSizes_dim2_dim3( - const std::vector>& pts, - std::size_t& groupSize_dim2, - std::size_t& groupSize_dim3, - float tol = 1e-5f -) { - const std::size_t n = pts.size(); - groupSize_dim2 = groupSize_dim3 = 0; - - if (n == 0) - return false; +bool +inferGroupSizes_dim2_dim3(const std::vector>& pts, + std::size_t& groupSize_dim2, + std::size_t& groupSize_dim3, + float tol = 1e-5f) +{ + const std::size_t n = pts.size(); + groupSize_dim2 = groupSize_dim3 = 0; + + if (n == 0) + return false; - if (n == 1) { - groupSize_dim2 = 1; - groupSize_dim3 = 1; - return true; + if (n == 1) + { + groupSize_dim2 = 1; + groupSize_dim3 = 1; + return true; } - // STIR CartesianCoordinate3D is (z, y, x) - const float x0 = pts[0].x(); - const float z0 = pts[0].z(); + // STIR CartesianCoordinate3D is (z, y, x) + const float x0 = pts[0].x(); + const float z0 = pts[0].z(); - // 1) Find how many initial points keep x and z the same - std::size_t runLen = 1; - while (runLen < n && - almostEqual(pts[runLen].x(), x0, tol) && - almostEqual(pts[runLen].z(), z0, tol)) { - ++runLen; + // 1) Find how many initial points keep x and z the same + std::size_t runLen = 1; + while (runLen < n && almostEqual(pts[runLen].x(), x0, tol) && almostEqual(pts[runLen].z(), z0, tol)) + { + ++runLen; } - groupSize_dim2 = runLen; + groupSize_dim2 = runLen; - // Must tile the full array - if (groupSize_dim2 == 0 || n % groupSize_dim2 != 0) - return false; + // Must tile the full array + if (groupSize_dim2 == 0 || n % groupSize_dim2 != 0) + return false; - groupSize_dim3 = n / groupSize_dim2; + groupSize_dim3 = n / groupSize_dim2; - // 2) Check each block of size groupSize_dim2 has constant x,z - for (std::size_t b = 0; b < groupSize_dim3; ++b) { - std::size_t start = b * groupSize_dim2; - float xb = pts[start].x(); - float zb = pts[start].z(); + // 2) Check each block of size groupSize_dim2 has constant x,z + for (std::size_t b = 0; b < groupSize_dim3; ++b) + { + std::size_t start = b * groupSize_dim2; + float xb = pts[start].x(); + float zb = pts[start].z(); - for (std::size_t i = 1; i < groupSize_dim2; ++i) { - const auto& p = pts[start + i]; - if (!almostEqual(p.x(), xb, tol) || - !almostEqual(p.z(), zb, tol)) { - return false; // pattern breaks inside a block + for (std::size_t i = 1; i < groupSize_dim2; ++i) + { + const auto& p = pts[start + i]; + if (!almostEqual(p.x(), xb, tol) || !almostEqual(p.z(), zb, tol)) + { + return false; // pattern breaks inside a block } } } - // 3) Optionally check that z actually changes between blocks - for (std::size_t b = 1; b < groupSize_dim3; ++b) { - float z_prev = pts[(b - 1) * groupSize_dim2].z(); - float z_curr = pts[b * groupSize_dim2].z(); - if (almostEqual(z_prev, z_curr, tol)) { - return false; // outer loop didn't move in z + // 3) Optionally check that z actually changes between blocks + for (std::size_t b = 1; b < groupSize_dim3; ++b) + { + float z_prev = pts[(b - 1) * groupSize_dim2].z(); + float z_curr = pts[b * groupSize_dim2].z(); + if (almostEqual(z_prev, z_curr, tol)) + { + return false; // outer loop didn't move in z } } - return true; + return true; } bool @@ -366,7 +479,7 @@ CListModeDataPETSIRD::isCylindricalConfiguration(const std::vector unique_tof_values; - find_uniqe_values_2D(unique_tof_values, scanner_info->tof_resolution); + vector_utils::find_unique_values_2D(unique_tof_values, scanner_info->tof_resolution); numberOfModules = replicated_module_list[0].NumberOfObjects(); numberOfElementsIndices = replicated_module_list[0].object.detecting_elements.NumberOfObjects(); @@ -378,7 +491,7 @@ CListModeDataPETSIRD::isCylindricalConfiguration(const std::vector& main_axis = getLargestVector(unique_dim1_values, unique_dim2_values, unique_dim3_values); + const std::set& main_axis = vector_utils::getLargestVector(unique_dim1_values, unique_dim2_values, unique_dim3_values); int num_transaxial_blocks = numberOfModules / main_axis.size(); info(format("I deduce that the scanner has {} transaxial number of blocks", num_transaxial_blocks)); @@ -398,10 +511,11 @@ CListModeDataPETSIRD::isCylindricalConfiguration(const std::vector block_angular_spacing; - if (!get_spacing_uniform(block_angular_spacing, unique_angle_modules, 1e-2)) /// epsilon * 10000) // relax epsilon here + if (!vector_utils::get_spacing_uniform( + block_angular_spacing, unique_angle_modules, 1e-2)) /// epsilon * 10000) // relax epsilon here return false; -std::size_t group2 = 0, group3 = 0; + std::size_t group2 = 0, group3 = 0; { std::vector> pet_sird_positions; const petsird::TypeOfModule type_of_module = replicated_module_list.size() - 1; @@ -418,37 +532,36 @@ std::size_t group2 = 0, group3 = 0; mean_coord.y() = +corner.c[1] / box_shape.corners.size(); mean_coord.z() = +corner.c[2] / box_shape.corners.size(); } - // mean_coord.z() += this_scanner_sptr->get_axial_crystal_spacing() / + // mean_coord.z() += this_scanner_sptr->get_axial_crystal_spacing() / // save mean pos into map pet_sird_positions.push_back(mean_coord); } - } - - std::cout << "Nikos here" << std::endl; - -if (inferGroupSizes_dim2_dim3(pet_sird_positions, group2, group3)) { - std::cout << "groupSize_dim2 = " << group2 << "\n"; - - std::cout << "groupSize_dim3 = " << group3 << "\n"; - -} else { - std::cout << "No (dim2, dim3) loop structure detected.\n"; - group2 = 1; - group3 = 1; -} + } - std::cerr << "Nikos here" << std::endl; + std::cout << "Nikos here" << std::endl; + if (inferGroupSizes_dim2_dim3(pet_sird_positions, group2, group3)) + { + std::cout << "groupSize_dim2 = " << group2 << "\n"; - } + std::cout << "groupSize_dim3 = " << group3 << "\n"; + } + else + { + std::cout << "No (dim2, dim3) loop structure detected.\n"; + group2 = 1; + group3 = 1; + } + std::cerr << "Nikos here" << std::endl; + } std::vector element_horizontal_spacing, element_vertical_spacing; std::set unique_elements_horizontal_values, unique_elements_vertical_values; if (radius_indx == 0) { - if (!get_spacing_uniform(element_horizontal_spacing, unique_elements_dim3_values)) + if (!vector_utils::get_spacing_uniform(element_horizontal_spacing, unique_elements_dim3_values)) return false; - if (!get_spacing_uniform(element_vertical_spacing, unique_elements_dim2_values)) + if (!vector_utils::get_spacing_uniform(element_vertical_spacing, unique_elements_dim2_values)) return false; unique_elements_horizontal_values = unique_elements_dim3_values; unique_elements_vertical_values = unique_elements_dim2_values; @@ -457,16 +570,21 @@ if (inferGroupSizes_dim2_dim3(pet_sird_positions, group2, group3)) { { error("TODO!"); } - - blocks_per_bucket_transaxial = group2 > 1 ? unique_elements_vertical_values.size()/group2 : group2; - std::cout << "blocks per bucket in transaxial direction = " << blocks_per_bucket_transaxial << "\n"; - blocks_per_bucket_axial = group3 > 1 ? unique_elements_horizontal_values.size()/(numberOfElementsIndices/group3) : group3; - std::cout << "blocks per bucket in axial direction = " << blocks_per_bucket_axial << "\n"; - num_axial_crystals_per_block = unique_elements_horizontal_values.size() / blocks_per_bucket_axial; - num_trans_crystals_per_block = unique_elements_vertical_values.size() / blocks_per_bucket_transaxial; +{ + for (size_t i = 0; i < tof_bin_edges.NumberOfBins(); ++i) + { + std::cout << "TOF bin edge " << i << ": " << tof_bin_edges.edges[i] << "\n"; + } +} + blocks_per_bucket_transaxial = group2 > 1 ? unique_elements_vertical_values.size() / group2 : group2; + std::cout << "blocks per bucket in transaxial direction = " << blocks_per_bucket_transaxial << "\n"; + blocks_per_bucket_axial = group3 > 1 ? unique_elements_horizontal_values.size() / (numberOfElementsIndices / group3) : group3; + std::cout << "blocks per bucket in axial direction = " << blocks_per_bucket_axial << "\n"; + num_axial_crystals_per_block = unique_elements_horizontal_values.size() / blocks_per_bucket_axial; + num_trans_crystals_per_block = unique_elements_vertical_values.size() / blocks_per_bucket_transaxial; std::vector block_axial_spacing; - get_spacing_uniform(block_axial_spacing, main_axis); + vector_utils::get_spacing_uniform(block_axial_spacing, main_axis); if (block_axial_spacing.size() < 1) { // std::set::iterator it = unique_elements_horizontal_values.begin(); @@ -479,114 +597,110 @@ if (inferGroupSizes_dim2_dim3(pet_sird_positions, group2, group3)) { info(format("I counted {} axial blocks with spacing {}", unique_dim3_values.size(), block_axial_spacing[0])); - // Check if the cyrcle area is less than 5% different from the polygon - float expected_circle_area = float(M_PI * radius * radius);; - float polygon_area = float(0.5f * unique_angle_modules.size() * radius * radius * std::sin(2.f * float(M_PI) / unique_angle_modules.size())); - info(format("Circle area: {}, Polygon area: {}, pct {}", expected_circle_area, polygon_area, std::abs(expected_circle_area - polygon_area) / expected_circle_area)); - if (std::abs(expected_circle_area - polygon_area) / expected_circle_area < 0.00001f) + // Check if the cyrcle area is less than 5% different from the polygon + float expected_circle_area = float(M_PI * radius * radius); + ; + float polygon_area + = float(0.5f * unique_angle_modules.size() * radius * radius * std::sin(2.f * float(M_PI) / unique_angle_modules.size())); + info(format("Circle area: {}, Polygon area: {}, pct {}", + expected_circle_area, + polygon_area, + std::abs(expected_circle_area - polygon_area) / expected_circle_area)); + if (std::abs(expected_circle_area - polygon_area) / expected_circle_area < 0.1f) { info("the cylindical area is more then 95% matching the polygon area. We will predsume a cylindrical configuration."); - this_scanner_sptr.reset( - new Scanner(Scanner::User_defined_scanner, - std::string("PETSIRD_defined_scanner"), - /* num dets per ring */ - (num_transaxial_blocks * unique_elements_vertical_values.size()), - unique_dim3_values.size() * unique_elements_horizontal_values.size() /* num of rings */, - /* number of non arccor bins */ - (num_transaxial_blocks * unique_elements_vertical_values.size()) / 2, - /* number of maximum arccor bins */ - (num_transaxial_blocks * unique_elements_vertical_values.size()) / 2, - /* inner ring radius */ - radius, - /* doi */ 4, // average_doi, - /* ring spacing */ - element_horizontal_spacing[0], //* 10.f, - // bin_size_v - element_vertical_spacing[0],// * 10.f, - /*intrinsic_tilt_v*/ - 0.f, - /*num_axial_blocks_per_bucket_v */ - blocks_per_bucket_axial, - /*num_transaxial_blocks_per_bucket_v*/ - blocks_per_bucket_transaxial, - /*num_axial_crystals_per_block_v*/ - num_axial_crystals_per_block, - /*num_transaxial_crystals_per_block_v*/ - num_trans_crystals_per_block, - /*num_axial_crystals_per_singles_unit_v*/ - unique_elements_horizontal_values.size() / blocks_per_bucket_axial, - /*num_transaxial_crystals_per_singles_unit_v*/ - unique_elements_vertical_values.size() / blocks_per_bucket_transaxial, - /*num_detector_layers_v*/ - 1, // num_detector_layers_v - scanner_info->energy_resolution_at_511.front(), // energy_resolution_v - 511 // reference_energy_v - // tof_bin_edges.NumberOfBins(), - // (tof_bin_edges.edges[1] - tof_bin_edges.edges[0])*10/2, - // *unique_tof_values.begin() * 10 // non-TOF - )); - - // 13, - // 4.056 * 1000 / 13, - // 555.F); // TODO singles info incorrect - - - // /* maximum number of timing bins */ - // tof_bin_edges.NumberOfBins(), - // /* size of basic TOF bin */ - // 10, - // /* Scanner's timing resolution */ - // *unique_tof_values.begin())); + this_scanner_sptr.reset(new Scanner(Scanner::User_defined_scanner, + std::string("PETSIRD_defined_scanner"), + /* num dets per ring */ + (num_transaxial_blocks * unique_elements_vertical_values.size()), + unique_dim3_values.size() * unique_elements_horizontal_values.size() /* num of rings */, + /* number of non arccor bins */ + (num_transaxial_blocks * unique_elements_vertical_values.size()) / 2, + /* number of maximum arccor bins */ + (num_transaxial_blocks * unique_elements_vertical_values.size()) / 2, + /* inner ring radius */ + radius, + /* doi */ average_doi, // average_doi, + /* ring spacing */ + element_horizontal_spacing[0], //* 10.f, + // bin_size_v + element_vertical_spacing[0], // * 10.f, + /*intrinsic_tilt_v*/ + 0.f, + /*num_axial_blocks_per_bucket_v */ + blocks_per_bucket_axial, + /*num_transaxial_blocks_per_bucket_v*/ + blocks_per_bucket_transaxial, + /*num_axial_crystals_per_block_v*/ + num_axial_crystals_per_block, + /*num_transaxial_crystals_per_block_v*/ + num_trans_crystals_per_block, + /*num_axial_crystals_per_singles_unit_v*/ + unique_elements_horizontal_values.size() / blocks_per_bucket_axial, + /*num_transaxial_crystals_per_singles_unit_v*/ + unique_elements_vertical_values.size() / blocks_per_bucket_transaxial, + /*num_detector_layers_v*/ + 1, // num_detector_layers_v + scanner_info->energy_resolution_at_511.front(), // energy_resolution_v + 511, // reference_energy_v + tof_bin_edges.NumberOfBins(), + (tof_bin_edges.edges[1] - tof_bin_edges.edges[0])/speed_of_light_in_mm_per_ps_div2, + *unique_tof_values.begin() * 10 // non-TOF + )); return true; } - else + else { info("the cylindical area is less then 95% matching the polygon area. We will predsume a non-cylindrical configuration."); this_scanner_sptr.reset( - new Scanner(Scanner::User_defined_scanner, - std::string("PETSIRD_defined_scanner"), - /* num dets per ring */ - (num_transaxial_blocks * unique_elements_vertical_values.size()), - unique_dim3_values.size() * unique_elements_horizontal_values.size() /* num of rings */, - /* number of non arccor bins */ - (num_transaxial_blocks * unique_elements_vertical_values.size()) / 2, - /* number of maximum arccor bins */ - (num_transaxial_blocks * unique_elements_vertical_values.size()) / 2, - /* inner ring radius */ - radius, - /* doi */ average_doi, - /* ring spacing */ - element_horizontal_spacing[0], //* 10.f, - // bin_size_v - element_vertical_spacing[0],// * 10.f, - /*intrinsic_tilt_v*/ - 0.f, - /*num_axial_blocks_per_bucket_v */ - blocks_per_bucket_axial, - /*num_transaxial_blocks_per_bucket_v*/ - blocks_per_bucket_transaxial, - /*num_axial_crystals_per_block_v*/ - num_axial_crystals_per_block, - /*num_transaxial_crystals_per_block_v*/ - num_trans_crystals_per_block, - /*num_axial_crystals_per_singles_unit_v*/ - unique_elements_horizontal_values.size() / blocks_per_bucket_axial, - /*num_transaxial_crystals_per_singles_unit_v*/ - unique_elements_vertical_values.size() / blocks_per_bucket_transaxial, - /*num_detector_layers_v*/ - 1, // num_detector_layers_v - scanner_info->energy_resolution_at_511.front(), // energy_resolution_v - 511, // reference_energy_v - 1, - 0.F, - 0.F, // non-TOF - "BlocksOnCylindrical", // scanner_geometry_v - *unique_elements_horizontal_values.begin(), // axial_crystal_spacing_v - std::round(*unique_elements_vertical_values.begin()), // transaxial_crystal_spacing_v - block_axial_spacing.front(), // axial_block_spacing_v - radius * block_angular_spacing.front(), // transaxial_block_spacing_v - "" // crystal_map_file_name_v - )); + new Scanner(Scanner::User_defined_scanner, + std::string("PETSIRD_defined_scanner"), + /* num dets per ring */ + (num_transaxial_blocks * unique_elements_vertical_values.size()), + unique_dim3_values.size() * unique_elements_horizontal_values.size() /* num of rings */, + /* number of non arccor bins */ + (num_transaxial_blocks * unique_elements_vertical_values.size()) / 2, + /* number of maximum arccor bins */ + (num_transaxial_blocks * unique_elements_vertical_values.size()) / 2, + /* inner ring radius */ + radius, + /* doi */ average_doi, + /* ring spacing */ + element_horizontal_spacing[0], //* 10.f, + // bin_size_v + element_vertical_spacing[0], // * 10.f, + /*intrinsic_tilt_v*/ + 0.f, + /*num_axial_blocks_per_bucket_v */ + 1, + /*num_transaxial_blocks_per_bucket_v*/ + 1, + /*num_axial_crystals_per_block_v*/ + blocks_per_bucket_axial * num_axial_crystals_per_block, + /*num_transaxial_crystals_per_block_v*/ + blocks_per_bucket_transaxial * num_trans_crystals_per_block, + /*num_axial_crystals_per_singles_unit_v*/ + unique_elements_horizontal_values.size() / blocks_per_bucket_axial, + /*num_transaxial_crystals_per_singles_unit_v*/ + unique_elements_vertical_values.size() / blocks_per_bucket_transaxial, + /*num_detector_layers_v*/ + 1, // num_detector_layers_v + scanner_info->energy_resolution_at_511.front(), // energy_resolution_v + 511, // reference_energy_v + 1, + 0.F, + 0.F, // non-TOF + "BlocksOnCylindrical", // scanner_geometry_v + (*std::next(unique_elements_horizontal_values.begin()) + - *unique_elements_horizontal_values.begin()), // axial_crystal_spacing_v + (*std::next(unique_elements_vertical_values.begin()) + - *unique_elements_vertical_values.begin()), // transaxial_crystal_spacing_v + (*std::next(unique_elements_horizontal_values.begin()) - *unique_elements_horizontal_values.begin()) + * num_axial_crystals_per_block * blocks_per_bucket_axial, // axial_block_spacing_v + (*std::next(unique_elements_vertical_values.begin()) - *unique_elements_vertical_values.begin()) + * num_trans_crystals_per_block * blocks_per_bucket_transaxial, // transaxial_block_spacing_v + "" // crystal_map_file_name_v + )); return false; } @@ -604,7 +718,7 @@ CListModeDataPETSIRD::CListModeDataPETSIRD(const std::string& listmode_filename, else current_lm_data_ptr.reset(new petsird::binary::PETSIRDReader(listmode_filename)); - m_has_delayeds = false; + m_has_delayeds = false; current_lm_data_ptr->ReadHeader(header); scanner_info = std::make_shared(header.scanner); @@ -621,161 +735,160 @@ CListModeDataPETSIRD::CListModeDataPETSIRD(const std::string& listmode_filename, curr_event_block = std::get(curr_time_block); else error("CListModeDataPETSIRD: holds_alternative not true. Abord."); - - if ( isCylindricalConfiguration(replicated_module_list)) - { - int tof_mash_factor = 1; - this->set_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())); - } - else - { - // this_scanner_sptr.get_ - DetectorCoordinateMap::det_pos_to_coord_type petsird_map; - const petsird::TypeOfModule type_of_module = replicated_module_list.size() - 1; - petsird_to_stir = std::make_shared(); - - enum class InnerLoopDim { Axial, Tangential, Radial }; - InnerLoopDim inner_dim = InnerLoopDim::Tangential; // determined from your groupSize analysis + bool is_cylindrical = isCylindricalConfiguration(replicated_module_list); + // if (is_cylindrical) + // { + // int tof_mash_factor = 1; + // this->set_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())); + // } + // else + // { + DetectorCoordinateMap::det_pos_to_coord_type petsird_map; + const petsird::TypeOfModule type_of_module = replicated_module_list.size() - 1; + + petsird_to_stir = std::make_shared(); + + enum class InnerLoopDim + { + Axial, + Tangential, + Radial + }; + InnerLoopDim inner_dim = InnerLoopDim::Tangential; // determined from your groupSize analysis - // PRECOMPUTED from previous step: - std::size_t groupSize = blocks_per_bucket_transaxial == 1 ? 0 : num_trans_crystals_per_block; // e.g. 5, or 1 if purely monotonic - // extern InnerLoopDim inner_dim; // Axial / Tangential / Radial + // PRECOMPUTED from previous step: + std::size_t groupSize + = blocks_per_bucket_transaxial == 1 ? 0 : num_trans_crystals_per_block; // e.g. 5, or 1 if purely monotonic + // extern InnerLoopDim inner_dim; // Axial / Tangential / Radial - const int num_ax = blocks_per_bucket_axial * num_axial_crystals_per_block; - const int num_tang = blocks_per_bucket_transaxial * num_trans_crystals_per_block; + const int num_ax = blocks_per_bucket_axial * num_axial_crystals_per_block; + const int num_tang = blocks_per_bucket_transaxial * num_trans_crystals_per_block; - int ind = 0; + int ind = 0; - for (uint32_t module = 0; module < numberOfModules; module++) - for (uint32_t elem = 0; elem < numberOfElementsIndices; elem++) - // for (uint32_t ener = 0; ener < num_event_energy_bins; ener++) //energy not supported yet - { - // ---- 1) Decompose elem into: tile, in-tile indices ---- - const uint32_t tileSize = groupSize * groupSize; // elems per tile - const uint32_t tiles_per_bucket = - blocks_per_bucket_axial * blocks_per_bucket_transaxial; + for (uint32_t module = 0; module < numberOfModules; module++) + for (uint32_t elem = 0; elem < numberOfElementsIndices; elem++) + // for (uint32_t ener = 0; ener < num_event_energy_bins; ener++) //energy not supported yet + { + // ---- 1) Decompose elem into: tile, in-tile indices ---- + const uint32_t tileSize = groupSize * groupSize; // elems per tile + const uint32_t tiles_per_bucket = blocks_per_bucket_axial * blocks_per_bucket_transaxial; - const uint32_t tile = (groupSize > 0 ? elem / tileSize : 0); // which tile - const uint32_t inTile = (groupSize > 0 ? elem % tileSize : elem); // index inside tile + const uint32_t tile = (groupSize > 0 ? elem / tileSize : 0); // which tile + const uint32_t inTile = (groupSize > 0 ? elem % tileSize : elem); // index inside tile - const uint32_t i0 = inTile % groupSize; // fast inside tile (local "x") - const uint32_t i1 = inTile / groupSize; // slow inside tile (local "y") + const uint32_t i0 = inTile % groupSize; // fast inside tile (local "x") + const uint32_t i1 = inTile / groupSize; // slow inside tile (local "y") - int ax_pos = 0; - int tang_pos = 0; - int rad_pos = 0; // ignored for now + int ax_pos = 0; + int tang_pos = 0; + int rad_pos = 0; // ignored for now - // ---- 2) Decode which block (tile) we are in along axial/tangential ---- - switch (inner_dim) - { - case InnerLoopDim::Tangential: - { - // Here we assume: - // - i0 runs tangential inside a block - // - i1 runs axial inside a block - // - // tiles are laid out as: - // tangential: blocks_per_bucket_transaxial tiles - // axial: blocks_per_bucket_axial tiles - - const uint32_t tang_block = tile % blocks_per_bucket_transaxial; - const uint32_t axial_block = tile / blocks_per_bucket_transaxial; - - tang_pos = static_cast(tang_block * groupSize + i0); - ax_pos = static_cast(axial_block * groupSize + i1); - break; - } + // ---- 2) Decode which block (tile) we are in along axial/tangential ---- + switch (inner_dim) + { + case InnerLoopDim::Tangential: { + // Here we assume: + // - i0 runs tangential inside a block + // - i1 runs axial inside a block + // + // tiles are laid out as: + // tangential: blocks_per_bucket_transaxial tiles + // axial: blocks_per_bucket_axial tiles + + const uint32_t tang_block = tile % blocks_per_bucket_transaxial; + const uint32_t axial_block = tile / blocks_per_bucket_transaxial; + + tang_pos = static_cast(tang_block * groupSize + i0); + ax_pos = static_cast(axial_block * groupSize + i1); + break; + } - case InnerLoopDim::Axial: - { - // Here we assume: - // - i0 runs axial inside a block - // - i1 runs tangential inside a block - // - // tiles are laid out as: - // axial: blocks_per_bucket_axial tiles - // tangential: blocks_per_bucket_transaxial tiles - - const uint32_t axial_block = tile % blocks_per_bucket_axial; - const uint32_t tang_block = tile / blocks_per_bucket_axial; - - ax_pos = static_cast(axial_block * groupSize + i0); - tang_pos = static_cast(tang_block * groupSize + i1); - break; - } - } + case InnerLoopDim::Axial: { + // Here we assume: + // - i0 runs axial inside a block + // - i1 runs tangential inside a block + // + // tiles are laid out as: + // axial: blocks_per_bucket_axial tiles + // tangential: blocks_per_bucket_transaxial tiles + + const uint32_t axial_block = tile % blocks_per_bucket_axial; + const uint32_t tang_block = tile / blocks_per_bucket_axial; + + ax_pos = static_cast(axial_block * groupSize + i0); + tang_pos = static_cast(tang_block * groupSize + i1); + break; + } + } - DetectionPosition<> detpos(tang_pos + module * (num_trans_crystals_per_block * blocks_per_bucket_transaxial), - ax_pos, rad_pos); + DetectionPosition<> detpos( + tang_pos + module * (num_trans_crystals_per_block * blocks_per_bucket_transaxial), ax_pos, rad_pos); - petsird::ExpandedDetectionBin expanded_detection_bin{ module, elem, 0 }; + petsird::ExpandedDetectionBin expanded_detection_bin{ module, elem, 0 }; - auto box_shape = - petsird_helpers::geometry::get_detecting_box( - *scanner_info, type_of_module, expanded_detection_bin); + if (this_scanner_sptr->get_scanner_geometry() == "Generic") + { + auto box_shape = petsird_helpers::geometry::get_detecting_box(*scanner_info, type_of_module, expanded_detection_bin); + CartesianCoordinate3D mean_coord(0.f, 0.f, 0.f); - CartesianCoordinate3D mean_coord(0.f, 0.f, 0.f); + for (auto& corner : box_shape.corners) + { + mean_coord.x() += corner.c[0] / box_shape.corners.size(); + mean_coord.y() += corner.c[1] / box_shape.corners.size(); + mean_coord.z() += corner.c[2] / box_shape.corners.size(); + } - for (auto& corner : box_shape.corners) - { - mean_coord.x() += corner.c[0] / box_shape.corners.size(); - mean_coord.y() += corner.c[1] / box_shape.corners.size(); - mean_coord.z() += corner.c[2] / box_shape.corners.size(); - } - // mean_coord.z() = (round(mean_coord.z() * 1000.0F)) / 1000.0F; - // mean_coord.y() = (round(mean_coord.y() * 1000.0F)) / 1000.0F; - // mean_coord.x() = (round(mean_coord.x() * 1000.0F)) / 1000.0F; + petsird_map[detpos] = mean_coord; - petsird_map[detpos] = mean_coord; + std::cout << ind << " : " << detpos.radial_coord() << ", " << detpos.axial_coord() << ", " + << detpos.tangential_coord() << ", " << mean_coord.x() << ", " << mean_coord.y() << ", " << mean_coord.z() + << "\n"; + ++ind; + } + else if (this_scanner_sptr->get_scanner_geometry() == "BlocksOnCylindrical" || is_cylindrical) + { + (*petsird_to_stir)[expanded_detection_bin] = detpos; + } - // auto detectionBin = petsird_helpers::make_detection_bin( - // *scanner_info, - // type_of_module, - // expanded_detection_bin); + // auto detectionBin = petsird_helpers::make_detection_bin( + // *scanner_info, + // type_of_module, + // expanded_detection_bin); // petsird_map[detectionBin] = mean_coord; - // Save to shared_ptr map - (*petsird_to_stir)[expanded_detection_bin] = detpos; - - std::cout << ind << " : " - << detpos.radial_coord() << ", " - << detpos.axial_coord() << ", " - << detpos.tangential_coord() << ", " - << mean_coord.x() << ", " - << mean_coord.y() << ", " - << mean_coord.z() << "\n"; - ++ind; - } - - // this->map.reset(new DetectorCoordinateMapLightPETSIRD(petsird_map)); - this->map.reset(new DetectorCoordinateMap(petsird_map)); - // this_scanner_sptr->get_detector_map_sptr()->set_detector_coordinate_map_light_sptr( - // std::make_shared(petsird_map)); - - this_scanner_sptr->set_detector_map(petsird_map); - - int tof_mash_factor = 1; - this->set_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())); + // Save to shared_ptr map + } - } + // // this->map.reset(new DetectorCoordinateMapLightPETSIRD(petsird_map)); + // this->map.reset(new DetectorCoordinateMap(petsird_map)); + // // this_scanner_sptr->get_detector_map_sptr()->set_detector_coordinate_map_light_sptr( + // // std::make_shared(petsird_map)); + // this->map->write_detectormap_to_file("petsird_detector_map_from_scanner_definition.txt"); + // this_scanner_sptr->set_detector_map(petsird_map); + // this_scanner_sptr->set_up(); + + int tof_mash_factor = 1; + this->set_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())); + // } shared_ptr _exam_info_sptr(new ExamInfo); // Only PET scanners supported @@ -807,10 +920,20 @@ CListModeDataPETSIRD::open_lm_file() const shared_ptr CListModeDataPETSIRD::get_empty_record_sptr() const { - shared_ptr sptr(new CListRecordPETSIRD(scanner_info, this_scanner_sptr, this->map, petsird_to_stir)); - std::dynamic_pointer_cast(sptr)->event().set_scanner_sptr( - this->get_proj_data_info_sptr()->get_scanner_sptr()); - std::dynamic_pointer_cast(sptr)->event().set_PETSIRD_ranges(numberOfModules, numberOfElementsIndices); + shared_ptr sptr(new CListRecordPETSIRD(scanner_info, this_scanner_sptr)); //, this->map, petsird_to_stir)); + if (this_scanner_sptr->get_scanner_geometry() == "Generic") + { + std::dynamic_pointer_cast(sptr)->event().set_map_sptr(this->map); + } + else if (this_scanner_sptr->get_scanner_geometry() == "BlocksOnCylindrical" || + this_scanner_sptr->get_scanner_geometry() == "Cylindrical") + { + std::dynamic_pointer_cast(sptr)->event().set_petsird_to_stir_map(petsird_to_stir); + } + + // std::dynamic_pointer_cast(sptr)->event().set_scanner_sptr( + // this->get_proj_data_info_sptr()->get_scanner_sptr()); + // std::dynamic_pointer_cast(sptr)->event().set_PETSIRD_ranges(numberOfModules, numberOfElementsIndices); // std::dynamic_pointer_cast(sptr)->event_PETSIRD().set_map_sptr(map); return sptr; From c33dbdabc1a42e7acf695950e811e9c95c8edb50 Mon Sep 17 00:00:00 2001 From: NikEfth Date: Tue, 16 Dec 2025 16:13:43 +0100 Subject: [PATCH 40/42] Update PETSIRD submodule to CMake export fixes --- PETSIRD | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/PETSIRD b/PETSIRD index 763832916b..d5a94dbaf9 160000 --- a/PETSIRD +++ b/PETSIRD @@ -1 +1 @@ -Subproject commit 763832916bab240e4ca8406ce23d1d274bf6852a +Subproject commit d5a94dbaf9b4898a14fc8510368094a26ac11640 From caa80e50a226dcc042861df92acaa9322b33b508 Mon Sep 17 00:00:00 2001 From: NikEfth Date: Tue, 16 Dec 2025 18:45:21 +0100 Subject: [PATCH 41/42] Update PETSIRD submodule --- PETSIRD | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/PETSIRD b/PETSIRD index d5a94dbaf9..640644a687 160000 --- a/PETSIRD +++ b/PETSIRD @@ -1 +1 @@ -Subproject commit d5a94dbaf9b4898a14fc8510368094a26ac11640 +Subproject commit 640644a687924147c798792984af727bcb97d2f1 From 7f6bcdfa9982ca416091719a7294e8e673fcc1a5 Mon Sep 17 00:00:00 2001 From: NikEfth Date: Wed, 17 Dec 2025 16:41:24 +0100 Subject: [PATCH 42/42] Major changes to be committed: * Moved all the PETSIRD geometry mapping code to the new class PETSIRDInfo * In this way we can share the mapping between BinNormalisationFromPETSIRD and CListModeDataPETSIRD * Minor fixes --- CMakeLists.txt | 22 +- src/IO/CMakeLists.txt | 4 - src/buildblock/CMakeLists.txt | 11 +- src/buildblock/PETSIRDInfo.cxx | 834 +++++++++++++++++ .../stir/IO/PETSIRDCListmodeInputFileFormat.h | 3 +- src/include/stir/PETSIRDInfo.h | 154 ++++ .../CListModeDataBasedOnCoordinateMap.h | 2 +- .../stir/listmode/CListModeDataPETSIRD.h | 125 +-- .../stir/listmode/CListRecordPETSIRD.h | 62 +- .../stir/listmode/CListRecordPETSIRD.inl | 25 +- .../BinNormalisationFromPETSIRD.h | 67 ++ .../CListModeDataPETSIRD.cxx | 842 +----------------- src/listmode_buildblock/CMakeLists.txt | 10 +- .../BinNormalisationFromPETSIRD.cxx | 74 ++ src/recon_buildblock/CMakeLists.txt | 18 + 15 files changed, 1263 insertions(+), 990 deletions(-) create mode 100644 src/buildblock/PETSIRDInfo.cxx create mode 100644 src/include/stir/PETSIRDInfo.h create mode 100644 src/include/stir/recon_buildblock/BinNormalisationFromPETSIRD.h create mode 100644 src/recon_buildblock/BinNormalisationFromPETSIRD.cxx diff --git a/CMakeLists.txt b/CMakeLists.txt index 533521edb2..a8c76ab913 100755 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -278,10 +278,28 @@ if(NOT DISABLE_PETSIRD) # Check if PETSIRD directory exists if(EXISTS "${PROJECT_SOURCE_DIR}/PETSIRD/cpp/CMakeLists.txt") + set(PETSIRD_CPP_DIR ${PROJECT_SOURCE_DIR}/PETSIRD/cpp) + set(PETSIRD_GENERATED_DIR ${PETSIRD_CPP_DIR}/generated/petsird) + + find_program(YARDL_EXECUTABLE yardl) + if(NOT YARDL_EXECUTABLE) + message(FATAL_ERROR "yardl not found but required for PETSIRD") + endif() + + if(NOT EXISTS "${PETSIRD_GENERATED_DIR}/CMakeLists.txt") + message(STATUS "Running yardl for PETSIRD...") + execute_process( + COMMAND just build + WORKING_DIRECTORY ${PROJECT_SOURCE_DIR}/PETSIRD + ) + if(NOT YARDL_RESULT EQUAL 0) + message(FATAL_ERROR "yardl generation failed") + endif() + endif() set(HAVE_PETSIRD TRUE) set(PETSIRD_dir ${PROJECT_SOURCE_DIR}/PETSIRD/cpp) add_subdirectory(${PETSIRD_dir} PETSIRD) - # Make PETSIRD headers globally available + # Make PETSIRD headers globally available include_directories(${PROJECT_SOURCE_DIR}/PETSIRD/cpp/generated) include_directories(${PROJECT_SOURCE_DIR}/PETSIRD/cpp/helpers/include) install(TARGETS petsird_generated @@ -289,7 +307,7 @@ if(NOT DISABLE_PETSIRD) DESTINATION lib) message(STATUS "PETSIRD support enabled") else() - message(FATAL_ERROR "PETSIRD directory not found. Please run: git submodule update --init --recursive") + message(FATAL_ERROR "PETSIRD directory not found") endif() endif() diff --git a/src/IO/CMakeLists.txt b/src/IO/CMakeLists.txt index 6a609236b7..987330d679 100644 --- a/src/IO/CMakeLists.txt +++ b/src/IO/CMakeLists.txt @@ -145,10 +145,6 @@ if (NOT MINI_STIR) endif() if(HAVE_PETSIRD) - target_include_directories(IO - PUBLIC - $ - ) target_link_libraries(IO PUBLIC petsird_generated) endif() diff --git a/src/buildblock/CMakeLists.txt b/src/buildblock/CMakeLists.txt index 8c9534f1b2..58b135ee55 100644 --- a/src/buildblock/CMakeLists.txt +++ b/src/buildblock/CMakeLists.txt @@ -107,7 +107,12 @@ if (NOT MINI_STIR) list(APPEND ${dir_LIB_SOURCES} ProjDataGEHDF5.cxx ) -endif() + endif() + if (HAVE_PETSIRD) + list(APPEND ${dir_LIB_SOURCES} + PETSIRDInfo.cxx + ) + endif() endif() # MINI_STIR @@ -153,3 +158,7 @@ endif() if (STIR_OPENMP) target_link_libraries(buildblock PUBLIC ${OpenMP_EXE_LINKER_FLAGS}) endif() + +if(HAVE_PETSIRD) + target_link_libraries(buildblock PUBLIC petsird_generated) +endif() \ No newline at end of file diff --git a/src/buildblock/PETSIRDInfo.cxx b/src/buildblock/PETSIRDInfo.cxx new file mode 100644 index 0000000000..23e068e265 --- /dev/null +++ b/src/buildblock/PETSIRDInfo.cxx @@ -0,0 +1,834 @@ +/* CListModeDataPETSIRD.cxx + +Coincidence LM Data Class for PETSIRD: Implementation + + Copyright 2025, UMCG + Copyright 2025, MGH / HST A. Martinos Center for Biomedical Imaging + + 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 + + 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. +*/ +/*! + +\file +\ingroup listmode +\brief implementation of class stir::CListModeDataPETSIRD + +\author Nikos Efthimiou +*/ + +#include "stir/PETSIRDInfo.h" +#include "stir/Succeeded.h" +#include +#include "stir/info.h" +#include "stir/warning.h" +#include "stir/error.h" + +#include "petsird_helpers.h" +#include "petsird_helpers/create.h" +#include "petsird_helpers/geometry.h" + +#include "petsird/binary/protocols.h" +#include "petsird/hdf5/protocols.h" + +START_NAMESPACE_STIR + +/*! + \namespace matrix + \brief Lightweight 3x3 matrix and 3D vector helpers used during PETSIRD geometry analysis. + + Provides utilities to: + - transpose a 3x3 matrix, + - subtract two 3x3 matrices, + - extract a rotation axis vector from the skew-symmetric part of a matrix. + + \details + - Mat3: std::array,3> for compact fixed-size storage. + - Vec3: std::array for simple 3D vectors. + - getAxisFromSkew(): + Given S = R - R^T (skew-symmetric part of a rotation matrix R), + returns the axis proportional to: + (S_z,y - S_y,z)/2, (S_x,z - S_z,x)/2, (S_y,x - S_x,y)/2. + For a pure rotation, S encodes the axis direction. + - These helpers assume small numerical noise; thresholds are handled by callers. + - No external dependencies; intended for quick geometric inference (e.g., rotation axis detection). +*/ +namespace matrix +{ + +using Mat3 = std::array, 3>; +using Vec3 = std::array; + +/*! + \brief Transpose a 3x3 matrix. + \param mat Input matrix. + \return Transposed matrix. +*/ +inline Mat3 +transpose(const Mat3& mat) +{ + std::array, 3> result{}; + for (int i = 0; i < 3; ++i) + for (int j = 0; j < 3; ++j) + result[j][i] = mat[i][j]; + return result; +} + +/*! + \brief Subtract two 3x3 matrices (A - B). + \param A Left-hand matrix. + \param B Right-hand matrix. + \return Result of A - B. +*/ +inline Mat3 +subtract(const Mat3& A, const Mat3& B) +{ + std::array, 3> result{}; + for (size_t i = 0; i < 3; ++i) + for (size_t j = 0; j < 3; ++j) + result[i][j] = A[i][j] - B[i][j]; + return result; +} + +/*! + \brief Extract rotation axis from the skew-symmetric matrix S = R - R^T. + \param S Skew-symmetric matrix. + \return Axis vector proportional to the rotation axis. +*/ +inline Vec3 +getAxisFromSkew(const Mat3& S) +{ + return { + 0.5f * (S[2][1] - S[1][2]), // x + 0.5f * (S[0][2] - S[2][0]), // y + 0.5f * (S[1][0] - S[0][1]) // z + }; +} + +} // namespace matrix + +/*! + \namespace vector_utils + \brief Helpers for spacing analysis and axis inference from coordinate sets. + + \details + - \ref vector_utils::get_spacing_uniform computes successive spacings between + sorted unique values and tests if they are uniform within a tolerance. + - \ref vector_utils::getLargestVector returns the largest of three coordinate + sets and logs which axis is inferred as “axial”. +*/ +namespace vector_utils +{ +/*! + \brief Compute spacings between sorted unique values and test uniformity. + \param spacing Output vector. Appends |x[i] - x[i-1]| for i=1..N-1, where x is the sorted version of \a unsorted_block_poss. + \param unsorted_block_poss Set of unique positions (e.g., angles or translations). + \param epsilon Tolerance for uniformity check (default 1e-4). + \return True if all spacings differ from the first spacing by <= \a epsilon, false otherwise. + + \details + - If \a spacing ends up empty (e.g., input size < 2), returns true (trivially uniform). +*/ +inline bool +get_spacing_uniform(std::vector& spacing, const std::set& unsorted_block_poss, double epsilon = 1e-4) +{ + std::vector sorted_z(unsorted_block_poss.begin(), unsorted_block_poss.end()); + for (size_t i = 1; i < sorted_z.size(); ++i) + { + spacing.push_back(std::abs(sorted_z[i] - sorted_z[i - 1])); + } + + return std::all_of(spacing.begin(), spacing.end(), [&](float s) { return std::abs(s - spacing.front()) <= epsilon; }); +} + +/*! + \brief Return the largest of three sets and report inferred axial direction. + \param x Values along X. + \param y Values along Y. + \param z Values along Z. + \return Const reference to the largest set among \a x, \a y, \a z. + + \details + Logs the index of the inferred axial direction: 0 (x), 1 (y), or 2 (z). +*/ +const std::set& +getLargestVector(const std::set& x, const std::set& y, const std::set& z) +{ + const std::set* largest = &x; + int axis = 0; + if (y.size() > largest->size()) + { + largest = &y; + axis = 1; + } + else if (z.size() > largest->size()) + { + largest = &z; + axis = 2; + } + + info(fmt::format("I believe the axial direction is the {}.", axis)); + return *largest; +} + +/*! + \brief Collect unique values from a 1D vector. + \param values Output set for unique values. + \param input Input vector. +*/ +void +find_unique_values_1D(std::set& values, const std::vector& input) +{ + for (float val : input) + { + // std::cout << val << std::endl; + values.insert(val); + } +} + +/*! + \brief Collect unique values from a 2D vector (matrix). + \param values Output set for unique values. + \param input Input 2D vector [rows][cols]. +*/ +void +find_unique_values_2D(std::set& values, const std::vector>& input) +{ + for (size_t row = 0; row < input.size(); ++row) + for (size_t col = 0; col < input[row].size(); ++col) + values.insert(input[row][col]); +} + +} // namespace vector_utils + +bool +almostEqual(double a, double b, double tol = 1e-6) +{ + return std::fabs(a - b) <= tol; +} + +// Detect groupSize along dim2 (y) and loops along dim3 (z) +// Returns true on success, false if pattern doesn't match the assumed structure. +bool +inferGroupSizes_dim2_dim3(const std::vector>& pts, + std::size_t& groupSize_dim2, + std::size_t& groupSize_dim3, + float tol = 1e-5f) +{ + const std::size_t n = pts.size(); + groupSize_dim2 = groupSize_dim3 = 0; + + if (n == 0) + return false; + + if (n == 1) + { + groupSize_dim2 = 1; + groupSize_dim3 = 1; + return true; + } + + // STIR CartesianCoordinate3D is (z, y, x) + const float x0 = pts[0].x(); + const float z0 = pts[0].z(); + + // 1) Find how many initial points keep x and z the same + std::size_t runLen = 1; + while (runLen < n && almostEqual(pts[runLen].x(), x0, tol) && almostEqual(pts[runLen].z(), z0, tol)) + { + ++runLen; + } + + groupSize_dim2 = runLen; + + // Must tile the full array + if (groupSize_dim2 == 0 || n % groupSize_dim2 != 0) + return false; + + groupSize_dim3 = n / groupSize_dim2; + + // 2) Check each block of size groupSize_dim2 has constant x,z + for (std::size_t b = 0; b < groupSize_dim3; ++b) + { + std::size_t start = b * groupSize_dim2; + float xb = pts[start].x(); + float zb = pts[start].z(); + + for (std::size_t i = 1; i < groupSize_dim2; ++i) + { + const auto& p = pts[start + i]; + if (!almostEqual(p.x(), xb, tol) || !almostEqual(p.z(), zb, tol)) + { + return false; // pattern breaks inside a block + } + } + } + + // 3) Optionally check that z actually changes between blocks + for (std::size_t b = 1; b < groupSize_dim3; ++b) + { + float z_prev = pts[(b - 1) * groupSize_dim2].z(); + float z_curr = pts[b * groupSize_dim2].z(); + if (almostEqual(z_prev, z_curr, tol)) + { + return false; // outer loop didn't move in z + } + } + + return true; +} + +/*! + \brief Infer scanner blocks and rotation axis from PETSIRD replicated modules. + \param unique_dim1_values Output set of unique translations along X. + \param unique_dim2_values Output set of unique translations along Y. + \param unique_dim3_values Output set of unique translations along Z. + \param replicated_module_list PETSIRD replicated detector modules. + \return Index of rotation axis (0=x, 1=y, 2=z) or -1 if not found. + + \details + - Extracts translation components into the provided sets. + - Uses skew-symmetric part of rotation matrices to infer axis direction. + - Emits warnings for mixed-axis rotations or inconsistencies. +*/ +int +PETSIRDInfo::figure_out_scanner_blocks_and_rotation_axis(std::set& unique_dim1_values, + std::set& unique_dim2_values, + std::set& unique_dim3_values) +{ + auto insertTranslations = [&](const petsird::RigidTransformation& trans) { + unique_dim1_values.insert(trans.matrix.at(0, 3)); + unique_dim2_values.insert(trans.matrix.at(1, 3)); + unique_dim3_values.insert(trans.matrix.at(2, 3)); + }; + + auto extractRotationMatrix = [](const petsird::RigidTransformation& trans) -> matrix::Mat3 { + matrix::Mat3 R; + for (int i = 0; i < 3; ++i) + for (int j = 0; j < 3; ++j) + R[i][j] = trans.matrix.at(i, j); + return R; + // skew = matrix::subtract(R, matrix::transpose(R)); + // auto rot = matrix::getAxisFromSkew(skew); + }; + + std::array, 3> skew; + int detected_axis = -1; + + std::vector replicated_module_list + = petsird_scanner_info_sptr->scanner_geometry.replicated_modules; + for (const auto& module : replicated_module_list) + for (const auto& mod_trans : module.transforms) + { + insertTranslations(mod_trans); + matrix::Mat3 R = extractRotationMatrix(mod_trans); + skew = matrix::subtract(R, matrix::transpose(R)); + auto axis_vec = matrix::getAxisFromSkew(skew); + + int current_axis = -1; + for (int i = 0; i < 3; ++i) + { + if (std::abs(axis_vec[i]) > 1e-6f) + { + if (current_axis != -1) + { + warning("Rotation involves multiple axis components. Possibly non-pure rotation."); + current_axis = -2; // Sentinel for mixed axes + return -1; + } + current_axis = i; + } + } + + if (current_axis >= 0) + { + if (detected_axis == -1) + detected_axis = current_axis; + else if (detected_axis != current_axis) + warning("Inconsistent rotation axis detected between modules."); + } + } + info(fmt::format("Rotation axis of blocks inferred as axis index {}", detected_axis)); + return detected_axis; +} + +/*! + \brief Determine block element translations and radius. + \param unique_dim1_values Output set of element translations along X. + \param unique_dim2_values Output set of element translations along Y. + \param unique_dim3_values Output set of element translations along Z. + \param radius Output inferred radius (positive component orthogonal to rotation axis). + \param radius_index Output index of the axis containing the radius component. + \param rotation_axis Known rotation axis (0=x, 1=y, 2=z). + \param replicated_module_list PETSIRD replicated detector modules. + + \details + - Scans element-level transforms to infer radius and collect translations. + - Emits warnings if mixed radii are detected. +*/ +void +PETSIRDInfo::figure_out_block_element_transformations(std::set& unique_dim1_values, + std::set& unique_dim2_values, + std::set& unique_dim3_values, + float& radius, + int& radius_index, + const int rotation_axis) +{ + + auto insert_translation = [&](const petsird::RigidTransformation& trans) { + unique_dim1_values.insert(trans.matrix.at(0, 3)); + unique_dim2_values.insert(trans.matrix.at(1, 3)); + unique_dim3_values.insert(trans.matrix.at(2, 3)); + }; + + auto detect_radius = [&](const petsird::RigidTransformation& trans) -> bool { + for (int i = 0; i < 3; ++i) + { + if (i == rotation_axis) + continue; + float candidate = trans.matrix.at(i, 3); + if (candidate > 0.0f) + { + radius = candidate; + radius_index = i; + return true; + } + } + return false; + }; + + std::vector replicated_module_list + = petsird_scanner_info_sptr->scanner_geometry.replicated_modules; + for (const auto& module : replicated_module_list) + { + for (const auto& el_trans : module.object.detecting_elements.transforms) + { + if (radius == 0.0f) + { + if (!detect_radius(el_trans)) + { + error("Unable to determine radius from translation components."); + continue; + } + } + else + { + float current = el_trans.matrix.at(radius_index, 3); + if (std::abs(current - radius) > 1e-4f) + warning("Mixed radii detected. Consider checking for misaligned modules."); + } + + insert_translation(el_trans); + // std::cout << el_trans.matrix << std::endl; + } + } +} + +void +PETSIRDInfo::figure_out_block_angles(std::set& unique_angle_modules, const int rot_axis) +{ + std::vector replicated_module_list + = petsird_scanner_info_sptr->scanner_geometry.replicated_modules; + for (const auto& module : replicated_module_list) + for (const auto& transform : module.transforms) + { + if (rot_axis == 0) + unique_angle_modules.insert( + std::fabs(int(1000.F * std::atan2(transform.matrix.at(1, 0), transform.matrix.at(2, 0))) / 1000.F)); + else if (rot_axis == 1) + unique_angle_modules.insert( + std::fabs(int(1000.F * std::atan2(transform.matrix.at(2, 0), transform.matrix.at(0, 0))) / 1000.F)); + else if (rot_axis == 2) + unique_angle_modules.insert( + std::fabs(int(1000.F * std::atan2(transform.matrix.at(1, 0), transform.matrix.at(0, 0))) / 1000.F)); + } +} + +PETSIRDInfo::PETSIRDInfo(shared_ptr petsird_info_sptr) + : petsird_scanner_info_sptr(petsird_info_sptr) +{ + + if (!petsird_scanner_info_sptr) + error("PETSIRDInfo: Null PETSIRD ScannerInformation pointer provided."); + + //! TODO: Determine the DOI based on material + float average_doi = 0.0; + if (petsird_scanner_info_sptr->bulk_materials.size() > 0) + { + const std::string& material = petsird_scanner_info_sptr->bulk_materials[0].name; + if (material.size() > 0) + average_doi = (material == "BGO") ? 5.0f : (material == "LSO" || material == "LYSO") ? 7.0f : 0.0f; + } + + const petsird::TypeOfModule type_of_module = petsird_scanner_info_sptr->scanner_geometry.replicated_modules.size() - 1; + + if (type_of_module > 0) + { + error("Multiple types of PETSIRD modules are not supported. Abord."); + } + + const auto& tof_bin_edges = petsird_scanner_info_sptr->tof_bin_edges[type_of_module][type_of_module]; + info(fmt::format("Num. of TOF bins in PETSIRD {}", tof_bin_edges.NumberOfBins())); + + std::set unique_tof_values; + vector_utils::find_unique_values_2D(unique_tof_values, petsird_scanner_info_sptr->tof_resolution); + //! TODO: Supports only single type of module + numberOfModules = petsird_scanner_info_sptr->scanner_geometry.replicated_modules[0].NumberOfObjects(); + //! TODO: Supports only single type of module + numberOfElementsIndices + = petsird_scanner_info_sptr->scanner_geometry.replicated_modules[0].object.detecting_elements.NumberOfObjects(); + std::set unique_dim1_values, unique_dim2_values, unique_dim3_values; + std::set unique_tof_resolutions; + + const int rotation_axis + = figure_out_scanner_blocks_and_rotation_axis(unique_dim1_values, unique_dim2_values, unique_dim3_values); + if (rotation_axis == -1) + is_cylindrical = false; + + const std::set& main_axis = vector_utils::getLargestVector(unique_dim1_values, unique_dim2_values, unique_dim3_values); + + int num_transaxial_blocks = numberOfModules / main_axis.size(); + info(fmt::format("I deduce that the scanner has {} transaxial number of blocks", num_transaxial_blocks)); + + float radius = 0; + int radius_indx = -1; + + std::set unique_elements_dim1_values, unique_elements_dim2_values, unique_elements_dim3_values; + figure_out_block_element_transformations( + unique_elements_dim1_values, unique_elements_dim2_values, unique_elements_dim3_values, radius, radius_indx, rotation_axis); + + std::set unique_angle_modules; + figure_out_block_angles(unique_angle_modules, rotation_axis); + + std::vector block_angular_spacing; + if (!vector_utils::get_spacing_uniform( + block_angular_spacing, unique_angle_modules, 1e-2)) /// epsilon * 10000) // relax epsilon here + is_cylindrical = false; + + std::size_t group2 = 0, group3 = 0; + { + std::vector> pet_sird_positions; + + for (uint32_t module = 0; module < 1; module++) + { + for (uint32_t elem = 0; elem < numberOfElementsIndices; elem++) + { + petsird::ExpandedDetectionBin expanded_detection_bin{ module, elem, 1 }; + auto box_shape = petsird_helpers::geometry::get_detecting_box( + *petsird_scanner_info_sptr, type_of_module, expanded_detection_bin); + CartesianCoordinate3D mean_coord; + for (auto& corner : box_shape.corners) + { // if STIR (z,y,x) -> PETSIRD (-y, -x, z) pheraps the order below needs to be changed + mean_coord.x() = +corner.c[0] / box_shape.corners.size(); + mean_coord.y() = +corner.c[1] / box_shape.corners.size(); + mean_coord.z() = +corner.c[2] / box_shape.corners.size(); + } + // mean_coord.z() += this_scanner_sptr->get_axial_crystal_spacing() / + // save mean pos into map + pet_sird_positions.push_back(mean_coord); + } + } + + if (inferGroupSizes_dim2_dim3(pet_sird_positions, group2, group3)) + { + std::cout << "groupSize_dim2 = " << group2 << "\n"; + + std::cout << "groupSize_dim3 = " << group3 << "\n"; + } + else + { + std::cout << "No (dim2, dim3) loop structure detected.\n"; + group2 = 1; + group3 = 1; + } + } + + std::vector element_horizontal_spacing, element_vertical_spacing; + std::set unique_elements_horizontal_values, unique_elements_vertical_values; + if (radius_indx == 0) + { + if (!vector_utils::get_spacing_uniform(element_horizontal_spacing, unique_elements_dim3_values)) + is_cylindrical = false; + if (!vector_utils::get_spacing_uniform(element_vertical_spacing, unique_elements_dim2_values)) + is_cylindrical = false; + unique_elements_horizontal_values = unique_elements_dim3_values; + unique_elements_vertical_values = unique_elements_dim2_values; + } + else + { + //! TODO: Multiple radii handling + error("Multiple radii handling not implemented yet."); + } + + { + info("Printing TOF bin edges for validation (please make sure that the STIR TOF bin edges match the PETSIRD TOF bin edges):"); + for (size_t i = 0; i < tof_bin_edges.NumberOfBins(); ++i) + { + std::cout << "PETSIRD TOF bin edge " << i << ": " << tof_bin_edges.edges[i] << "\n"; + } + } + + blocks_per_bucket_transaxial = group2 > 1 ? unique_elements_vertical_values.size() / group2 : group2; + std::cout << "blocks per bucket in transaxial direction = " << blocks_per_bucket_transaxial << "\n"; + blocks_per_bucket_axial = group3 > 1 ? unique_elements_horizontal_values.size() / (numberOfElementsIndices / group3) : group3; + std::cout << "blocks per bucket in axial direction = " << blocks_per_bucket_axial << "\n"; + num_axial_crystals_per_block = unique_elements_horizontal_values.size() / blocks_per_bucket_axial; + num_trans_crystals_per_block = unique_elements_vertical_values.size() / blocks_per_bucket_transaxial; + + std::vector block_axial_spacing; + vector_utils::get_spacing_uniform(block_axial_spacing, main_axis); + if (block_axial_spacing.size() < 1) + { + // std::set::iterator it = unique_elements_horizontal_values.begin(); + // std::advance(it,0); + float begin = *std::next(unique_elements_horizontal_values.begin(), 0); + // std::advance(it,unique_elements_horizontal_values.size()-1); + float end = *std::next(unique_elements_horizontal_values.begin(), unique_elements_horizontal_values.size() - 1); + block_axial_spacing.push_back(std::abs(end - begin)); + } + + info(fmt::format("I counted {} axial blocks with spacing {}", unique_dim3_values.size(), block_axial_spacing[0])); + // Check if the cyrcle area is less than 5% different from the polygon + float expected_circle_area = float(M_PI * radius * radius); + float polygon_area + = float(0.5f * unique_angle_modules.size() * radius * radius * std::sin(2.f * float(M_PI) / unique_angle_modules.size())); + info(fmt::format("Circle area: {}, Polygon area: {}, pct {}", + expected_circle_area, + polygon_area, + std::abs(expected_circle_area - polygon_area) / expected_circle_area)); + + if (std::abs(expected_circle_area - polygon_area) / expected_circle_area < 0.000000001f) + { + info("the cylindical area is more then 95% matching the polygon area. We will predsume a cylindrical configuration."); + stir_scanner_sptr.reset(new Scanner(Scanner::User_defined_scanner, + std::string("PETSIRD_defined_scanner"), + /* num dets per ring */ + (num_transaxial_blocks * unique_elements_vertical_values.size()), + unique_dim3_values.size() * unique_elements_horizontal_values.size() /* num of rings */, + /* number of non arccor bins */ + (num_transaxial_blocks * unique_elements_vertical_values.size()) / 2, + /* number of maximum arccor bins */ + (num_transaxial_blocks * unique_elements_vertical_values.size()) / 2, + /* inner ring radius */ + radius, + /* doi */ average_doi, // average_doi, + /* ring spacing */ + element_horizontal_spacing[0], //* 10.f, + // bin_size_v + element_vertical_spacing[0], // * 10.f, + /*intrinsic_tilt_v*/ + 0.f, + /*num_axial_blocks_per_bucket_v */ + blocks_per_bucket_axial, + /*num_transaxial_blocks_per_bucket_v*/ + blocks_per_bucket_transaxial, + /*num_axial_crystals_per_block_v*/ + num_axial_crystals_per_block, + /*num_transaxial_crystals_per_block_v*/ + num_trans_crystals_per_block, + /*num_axial_crystals_per_singles_unit_v*/ + unique_elements_horizontal_values.size() / blocks_per_bucket_axial, + /*num_transaxial_crystals_per_singles_unit_v*/ + unique_elements_vertical_values.size() / blocks_per_bucket_transaxial, + /*num_detector_layers_v*/ + 1, // num_detector_layers_v + petsird_scanner_info_sptr->energy_resolution_at_511.front(), // energy_resolution_v + 511, // reference_energy_v + tof_bin_edges.NumberOfBins(), + (tof_bin_edges.edges[1] - tof_bin_edges.edges[0]) / speed_of_light_in_mm_per_ps_div2, + *unique_tof_values.begin() * 10 // non-TOF + )); + is_cylindrical = true; + } + else + { + info("the cylindical area is less then 95% matching the polygon area. We will predsume a non-cylindrical configuration."); + stir_scanner_sptr.reset( + new Scanner(Scanner::User_defined_scanner, + std::string("PETSIRD_defined_scanner"), + /* num dets per ring */ + (num_transaxial_blocks * unique_elements_vertical_values.size()), + unique_dim3_values.size() * unique_elements_horizontal_values.size() /* num of rings */, + /* number of non arccor bins */ + (num_transaxial_blocks * unique_elements_vertical_values.size()) / 2, + /* number of maximum arccor bins */ + (num_transaxial_blocks * unique_elements_vertical_values.size()) / 2, + /* inner ring radius */ + radius, + /* doi */ average_doi, + /* ring spacing */ + element_horizontal_spacing[0], //* 10.f, + // bin_size_v + element_vertical_spacing[0], // * 10.f, + /*intrinsic_tilt_v*/ + 0.f, + /*num_axial_blocks_per_bucket_v */ + 1, + /*num_transaxial_blocks_per_bucket_v*/ + 1, + /*num_axial_crystals_per_block_v*/ + blocks_per_bucket_axial * num_axial_crystals_per_block, + /*num_transaxial_crystals_per_block_v*/ + blocks_per_bucket_transaxial * num_trans_crystals_per_block, + /*num_axial_crystals_per_singles_unit_v*/ + unique_elements_horizontal_values.size() / blocks_per_bucket_axial, + /*num_transaxial_crystals_per_singles_unit_v*/ + unique_elements_vertical_values.size() / blocks_per_bucket_transaxial, + /*num_detector_layers_v*/ + 1, // num_detector_layers_v + petsird_scanner_info_sptr->energy_resolution_at_511.front(), // energy_resolution_v + 511, // reference_energy_v + 1, + 0.F, + 0.F, // non-TOF + "BlocksOnCylindrical", // scanner_geometry_v + (*std::next(unique_elements_horizontal_values.begin()) + - *unique_elements_horizontal_values.begin()), // axial_crystal_spacing_v + (*std::next(unique_elements_vertical_values.begin()) + - *unique_elements_vertical_values.begin()), // transaxial_crystal_spacing_v + (*std::next(unique_elements_horizontal_values.begin()) - *unique_elements_horizontal_values.begin()) + * num_axial_crystals_per_block * blocks_per_bucket_axial, // axial_block_spacing_v + (*std::next(unique_elements_vertical_values.begin()) - *unique_elements_vertical_values.begin()) + * num_trans_crystals_per_block * blocks_per_bucket_transaxial, // transaxial_block_spacing_v + "" // crystal_map_file_name_v + )); + is_cylindrical = false; + } + + /// Now let's create the PETISIRD - STIR geometry mapping + petsird_to_stir = std::make_shared(); + petsird_map_sptr = std::make_shared(); + + enum class InnerLoopDim + { + Axial, + Tangential, + Radial + }; + InnerLoopDim inner_dim = InnerLoopDim::Tangential; // determined from your groupSize analysis + + // PRECOMPUTED from previous step: + std::size_t groupSize + = blocks_per_bucket_transaxial == 1 ? 0 : num_trans_crystals_per_block; // e.g. 5, or 1 if purely monotonic + // extern InnerLoopDim inner_dim; // Axial / Tangential / Radial + + const int num_ax = blocks_per_bucket_axial * num_axial_crystals_per_block; + const int num_tang = blocks_per_bucket_transaxial * num_trans_crystals_per_block; + + for (uint32_t module = 0; module < numberOfModules; module++) + for (uint32_t elem = 0; elem < numberOfElementsIndices; elem++) + // for (uint32_t ener = 0; ener < num_event_energy_bins; ener++) //energy not supported yet + { + // ---- 1) Decompose elem into: tile, in-tile indices ---- + const uint32_t tileSize = groupSize * groupSize; // elems per tile + const uint32_t tiles_per_bucket = blocks_per_bucket_axial * blocks_per_bucket_transaxial; + + const uint32_t tile = (groupSize > 0 ? elem / tileSize : 0); // which tile + const uint32_t inTile = (groupSize > 0 ? elem % tileSize : elem); // index inside tile + + const uint32_t i0 = inTile % groupSize; // fast inside tile (local "x") + const uint32_t i1 = inTile / groupSize; // slow inside tile (local "y") + + int ax_pos = 0; + int tang_pos = 0; + int rad_pos = 0; // ignored for now + + // ---- 2) Decode which block (tile) we are in along axial/tangential ---- + switch (inner_dim) + { + case InnerLoopDim::Tangential: { + // Here we assume: + // - i0 runs tangential inside a block + // - i1 runs axial inside a block + // + // tiles are laid out as: + // tangential: blocks_per_bucket_transaxial tiles + // axial: blocks_per_bucket_axial tiles + + const uint32_t tang_block = tile % blocks_per_bucket_transaxial; + const uint32_t axial_block = tile / blocks_per_bucket_transaxial; + + tang_pos = static_cast(tang_block * groupSize + i0); + ax_pos = static_cast(axial_block * groupSize + i1); + break; + } + + case InnerLoopDim::Axial: { + // Here we assume: + // - i0 runs axial inside a block + // - i1 runs tangential inside a block + // + // tiles are laid out as: + // axial: blocks_per_bucket_axial tiles + // tangential: blocks_per_bucket_transaxial tiles + + const uint32_t axial_block = tile % blocks_per_bucket_axial; + const uint32_t tang_block = tile / blocks_per_bucket_axial; + + ax_pos = static_cast(axial_block * groupSize + i0); + tang_pos = static_cast(tang_block * groupSize + i1); + break; + } + case InnerLoopDim::Radial: { + error("Radial inner loop not supported yet."); + break; + } + } + + DetectionPosition<> detpos( + tang_pos + module * (num_trans_crystals_per_block * blocks_per_bucket_transaxial), ax_pos, rad_pos); + + petsird::ExpandedDetectionBin expanded_detection_bin{ module, elem, 0 }; + + if (stir_scanner_sptr->get_scanner_geometry() == "Generic") + { + auto box_shape = petsird_helpers::geometry::get_detecting_box( + *petsird_scanner_info_sptr, type_of_module, expanded_detection_bin); + CartesianCoordinate3D mean_coord(0.f, 0.f, 0.f); + + for (auto& corner : box_shape.corners) + { + mean_coord.x() += corner.c[0] / box_shape.corners.size(); + mean_coord.y() += corner.c[1] / box_shape.corners.size(); + mean_coord.z() += corner.c[2] / box_shape.corners.size(); + } + + (*petsird_map_sptr)[detpos] = mean_coord; + + std::cout << detpos.radial_coord() << ", " << detpos.axial_coord() << ", " << detpos.tangential_coord() << ", " + << mean_coord.x() << ", " << mean_coord.y() << ", " << mean_coord.z() << "\n"; + } + else if (stir_scanner_sptr->get_scanner_geometry() == "BlocksOnCylindrical" || is_cylindrical) + { + (*petsird_to_stir)[expanded_detection_bin] = detpos; + } + + // auto detectionBin = petsird_helpers::make_detection_bin( + // *scanner_info, + // type_of_module, + // expanded_detection_bin); + + // petsird_map[detectionBin] = mean_coord; + + // Save to shared_ptr map + } + + // // this->map.reset(new DetectorCoordinateMapLightPETSIRD(petsird_map)); + // this->map.reset(new DetectorCoordinateMap(petsird_map)); + // // this_scanner_sptr->get_detector_map_sptr()->set_detector_coordinate_map_light_sptr( + // // std::make_shared(petsird_map)); + // this->map->write_detectormap_to_file("petsird_detector_map_from_scanner_definition.txt"); + // this_scanner_sptr->set_detector_map(petsird_map); + // this_scanner_sptr->set_up(); +} + +END_NAMESPACE_STIR \ No newline at end of file diff --git a/src/include/stir/IO/PETSIRDCListmodeInputFileFormat.h b/src/include/stir/IO/PETSIRDCListmodeInputFileFormat.h index 82840129d6..ff291dbaa0 100644 --- a/src/include/stir/IO/PETSIRDCListmodeInputFileFormat.h +++ b/src/include/stir/IO/PETSIRDCListmodeInputFileFormat.h @@ -2,8 +2,7 @@ Class defining input file format for coincidence listmode data for PETSIRD. - Copyright 2015 ETH Zurich, Institute of Particle Physics - Copyright 2020 Positrigo AG, Zurich + Copyright 2025, UMCG Copyright 2025 National Physical Laboratory Licensed under the Apache License, Version 2.0 (the "License"); diff --git a/src/include/stir/PETSIRDInfo.h b/src/include/stir/PETSIRDInfo.h new file mode 100644 index 0000000000..b3af2bc977 --- /dev/null +++ b/src/include/stir/PETSIRDInfo.h @@ -0,0 +1,154 @@ +/* CListModeDataPETSIRD.h + +Coincidence LM Data Class for PETSIRD + + Copyright 2025, UMCG + + 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 + + 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. + + */ + +/*! + +\file +\ingroup listmode +\brief Place that converts PETSIRD geometry to STIR geometry + +\author Nikos Efthimiou +*/ + +#include "petsird/protocols.h" +#include "stir/Scanner.h" +#include "stir/DetectorCoordinateMap.h" +#include + +START_NAMESPACE_STIR + +/*! + \brief Comparator for ordering petsird::ExpandedDetectionBin in std::map. + + \details + Orders by module_index, then element_index, then energy_index. +*/ +struct ExpandedDetectionBinLess +{ + + bool operator()(const petsird::ExpandedDetectionBin& a, const petsird::ExpandedDetectionBin& b) const + { + // Adjust field names if needed (I assume: module, element, energy_bin) + if (a.module_index < b.module_index) + return true; + if (a.module_index > b.module_index) + return false; + + if (a.element_index < b.element_index) + return true; + if (a.element_index > b.element_index) + return false; + return a.energy_index < b.energy_index; + } +}; + +/*! + \brief Mapping type from PETSIRD ExpandedDetectionBin to STIR DetectionPosition. +*/ +using PETSIRDToSTIRDetectorIndexMap = std::map, ExpandedDetectionBinLess>; + +/*! + \brief Class to hold PETSIRD-related information for STIR and do any necessary conversions. +*/ +class PETSIRDInfo +{ +public: + explicit PETSIRDInfo(shared_ptr); + + // void initialize(); + + inline std::shared_ptr get_scanner_sptr() const + { + return stir_scanner_sptr; + } + + inline shared_ptr get_petsird_to_stir_map() const + { + return petsird_to_stir; + } + + inline shared_ptr get_petsird_map_sptr() const + { + return petsird_map_sptr; + } + + bool is_cylindrical_configuration() { return is_cylindrical; }; + +private: + /*! + \brief Infer scanner blocks and rotation axis from PETSIRD replicated modules. + \param unique_dim1_values Output set of unique translations along X. + \param unique_dim2_values Output set of unique translations along Y. + \param unique_dim3_values Output set of unique translations along Z. + \param replicated_module_list PETSIRD replicated detector modules. + \return Index of rotation axis (0=x, 1=y, 2=z) or -1 if not found. + */ + int figure_out_scanner_blocks_and_rotation_axis(std::set& unique_dim1_values, + std::set& unique_dim2_values, + std::set& unique_dim3_values); + /*! + \brief Compute unique module rotation angles around the given axis. + \param unique_angle_modules Output set of unique angles (radians). + \param rot_axis Rotation axis index (0=x, 1=y, 2=z). + */ + void figure_out_block_angles(std::set& unique_angle_modules, const int rot_axis); + /*! + \brief Determine block element translations and radius. + \param unique_dim1_values Output set of element translations along X. + \param unique_dim2_values Output set of element translations along Y. + \param unique_dim3_values Output set of element translations along Z. + \param radius Output inferred radius. + \param radius_index Output axis index containing the radius component. + \param rotation_axis Known rotation axis (0=x, 1=y, 2=z). + \param replicated_module_list PETSIRD replicated detector modules. +*/ + void figure_out_block_element_transformations(std::set& unique_dim1_values, + std::set& unique_dim2_values, + std::set& unique_dim3_values, + float& radius, + int& radius_index, + const int rotation_axis); + + //! Scanner information as provided by PETSIRD. + shared_ptr petsird_scanner_info_sptr; + //! Active scanner instance. + shared_ptr stir_scanner_sptr; + + //! Number of replicated modules. + uint32_t numberOfModules; + //! Number of element indices per module. + uint32_t numberOfElementsIndices; + //! Transaxial blocks per bucket (scanner metadata). + uint32_t blocks_per_bucket_transaxial; + //! Axial blocks per bucket (scanner metadata). + uint32_t blocks_per_bucket_axial; + //! Number of axial crystals per block. + uint32_t num_axial_crystals_per_block; + //! Number of transaxial crystals per block. + uint32_t num_trans_crystals_per_block; + + bool is_cylindrical = true; + //! Mapping from PETSIRD expanded bins to STIR detection positions. + shared_ptr petsird_to_stir; + //! Mapping from STIR detection positions to PETSIRD coordinates. + shared_ptr petsird_map_sptr; +}; + +END_NAMESPACE_STIR \ No newline at end of file diff --git a/src/include/stir/listmode/CListModeDataBasedOnCoordinateMap.h b/src/include/stir/listmode/CListModeDataBasedOnCoordinateMap.h index babffd05db..05cc87e845 100644 --- a/src/include/stir/listmode/CListModeDataBasedOnCoordinateMap.h +++ b/src/include/stir/listmode/CListModeDataBasedOnCoordinateMap.h @@ -43,7 +43,7 @@ #include "stir/shared_ptr.h" // #include "stir/listmode/CListRecordSAFIR.h" -#include "stir/DetectorCoordinateMap.h" + START_NAMESPACE_STIR diff --git a/src/include/stir/listmode/CListModeDataPETSIRD.h b/src/include/stir/listmode/CListModeDataPETSIRD.h index 181278855f..7e10311754 100644 --- a/src/include/stir/listmode/CListModeDataPETSIRD.h +++ b/src/include/stir/listmode/CListModeDataPETSIRD.h @@ -32,47 +32,15 @@ Coincidence LM Data Class for PETSIRD #ifndef __stir_listmode_CListModeDataPETSIRD_H__ #define __stir_listmode_CListModeDataPETSIRD_H__ -#include #include "stir/listmode/CListModeDataBasedOnCoordinateMap.h" #include "stir/ProjData.h" #include "stir/listmode/CListRecord.h" #include "stir/shared_ptr.h" -#include "petsird/protocols.h" +#include "stir/PETSIRDInfo.h" START_NAMESPACE_STIR -/*! - \brief Comparator for ordering petsird::ExpandedDetectionBin in std::map. - - \details - Orders by module_index, then element_index, then energy_index. -*/ -struct ExpandedDetectionBinLess -{ - - bool operator()(const petsird::ExpandedDetectionBin& a, - const petsird::ExpandedDetectionBin& b) const - { - // Adjust field names if needed (I assume: module, element, energy_bin) - if (a.module_index < b.module_index) return true; - if (a.module_index > b.module_index) return false; - - if (a.element_index < b.element_index) return true; - if (a.element_index > b.element_index) return false; - return a.energy_index < b.energy_index; - } -}; - -/*! - \brief Mapping type from PETSIRD ExpandedDetectionBin to STIR DetectionPosition. -*/ -using PETSIRDToSTIRMap = std::map< - petsird::ExpandedDetectionBin, - stir::DetectionPosition<>, - ExpandedDetectionBinLess ->; - /*! \class CListModeDataPETSIRD \brief Reader for PETSIRD listmode data supporting variable geometry. @@ -84,28 +52,29 @@ using PETSIRDToSTIRMap = std::map< - Cylindrical → creates cylindrical scanner. - Block-based → creates block-based scanner. - Otherwise → creates generic scanner using crystal positions. - - Builds a DetectorCoordinateMap when needed and stores to disk. + - Builds a DetectorCoordinateMap when needed and stores to disk. \par - Infering the scanner geometry makes a lot of assumptions about what PET is. + Infering the scanner geometry makes a lot of assumptions about what PET is. In particular, it assumes: \li A PET scanner is made of rings of detectors \li The largest axis is the axial one. - \li So far we support only a single layer. This is partly hard-coded for simplicity. (look in the code for relevant TODOs and comments.) + \li So far we support only a single layer. This is partly hard-coded for simplicity. (look in the code for relevant TODOs and + comments.) \li Some of the hardcoded assumptions are in CListRecordPETSIRD as well. \note Exact PETSIRD format specification is defined in the PETSIRD project documentation. (NOTE: It looks like GATE.) - \note Initially, I wanted to: + \note Initially, I wanted to: - Is close to a cylindrical geometry ? - - then yes use a cylindrical scanner that is simpler. + - then yes use a cylindrical scanner that is simpler. - Else, is it made of blocks arranged on a cylinder. - However, now I do the following: - - Is close to cylindriacl geometry? + However, now I do the following: + - Is close to cylindriacl geometry? - yes use cylindrical scanner - - Check if blocks-on-cylinder configuration, are a good match. + - Check if blocks-on-cylinder configuration, are a good match. - yes use blocks-on-cylinder scanner - - else use generic scanner and export the map to the disk. + - else use generic scanner and export the map to the disk. If listmode reconstruciton is done, the map is regenerated on-the-fly. @@ -145,78 +114,20 @@ class CListModeDataPETSIRD : public CListModeDataBasedOnCoordinateMap mutable petsird::TimeBlock curr_time_block; - //! Number of replicated modules. - int numberOfModules; - //! Number of element indices per module. - int numberOfElementsIndices; - //! Transaxial blocks per bucket (scanner metadata). - int blocks_per_bucket_transaxial; - //! Axial blocks per bucket (scanner metadata). - int blocks_per_bucket_axial; - //! Number of axial crystals per block. - int num_axial_crystals_per_block; - //! Number of transaxial crystals per block. - int num_trans_crystals_per_block; - mutable petsird::EventTimeBlock curr_event_block; - //! Mapping from PETSIRD expanded bins to STIR detection positions. - shared_ptr petsird_to_stir; + //! Active module pair (prompt/delayed, or two modules for coincidences). //! \todo: This hard-codes a single/matterial layer detector assumption. petsird::TypeOfModulePair type_of_module_pair{ 0, 0 }; - //! Active scanner instance. - shared_ptr this_scanner_sptr; - //! Scanner information as provided by PETSIRD. - shared_ptr scanner_info; + //! Current event prompt flag. mutable bool curr_is_prompt = true; //! Whether delayed events are present. - mutable bool m_has_delayeds; - /*! - \brief Detect if the PETSIRD geometry is cylindrical and initialise scanner/map accordingly. - \param replicated_module_list PETSIRD replicated detector modules. - \return True if cylindrical configuration was detected. - */ - bool isCylindricalConfiguration(const std::vector& replicated_module_list); - - /*! - \brief Infer scanner blocks and rotation axis from PETSIRD replicated modules. - \param unique_dim1_values Output set of unique translations along X. - \param unique_dim2_values Output set of unique translations along Y. - \param unique_dim3_values Output set of unique translations along Z. - \param replicated_module_list PETSIRD replicated detector modules. - \return Index of rotation axis (0=x, 1=y, 2=z) or -1 if not found. - */ - int figure_out_scanner_blocks_and_rotation_axis(std::set& unique_dim1_values, - std::set& unique_dim2_values, - std::set& unique_dim3_values, - const std::vector& replicated_module_list); - /*! - \brief Determine block element translations and radius. - \param unique_dim1_values Output set of element translations along X. - \param unique_dim2_values Output set of element translations along Y. - \param unique_dim3_values Output set of element translations along Z. - \param radius Output inferred radius. - \param radius_index Output axis index containing the radius component. - \param rotation_axis Known rotation axis (0=x, 1=y, 2=z). - \param replicated_module_list PETSIRD replicated detector modules. - */ - void figure_out_block_element_transformations(std::set& unique_dim1_values, - std::set& unique_dim2_values, - std::set& unique_dim3_values, - float& radius, - int& radius_index, - const int rotation_axis, - const std::vector& replicated_module_list); - /*! - \brief Compute unique module rotation angles around the given axis. - \param unique_angle_modules Output set of unique angles (radians). - \param rot_axis Rotation axis index (0=x, 1=y, 2=z). - \param replicated_module_list PETSIRD replicated detector modules. - */ - void figure_out_block_angles(std::set& unique_angle_modules, - const int rot_axis, - const std::vector& replicated_module_list); + mutable bool m_has_delayeds; + + shared_ptr scanner_info; + + shared_ptr petsird_info_sptr; }; END_NAMESPACE_STIR diff --git a/src/include/stir/listmode/CListRecordPETSIRD.h b/src/include/stir/listmode/CListRecordPETSIRD.h index fb8d319aaa..e96c3471be 100644 --- a/src/include/stir/listmode/CListRecordPETSIRD.h +++ b/src/include/stir/listmode/CListRecordPETSIRD.h @@ -2,7 +2,7 @@ Coincidence Event Class for PETSIRD: Header File - Copyright 2025, UMCG + Copyright 2025, UMCG Copyright 2025 National Physical Laboratory Licensed under the Apache License, Version 2.0 (the "License"); @@ -36,13 +36,9 @@ Coincidence Event Class for PETSIRD: Header File #include "stir/DetectionPositionPair.h" #include "stir/Succeeded.h" #include "stir/ByteOrderDefine.h" -#include "petsird_helpers.h" #include "boost/cstdint.hpp" #include "stir/DetectorCoordinateMap.h" -//#include "petsird/types.h" - -// #include "../../PETSIRD/cpp/generated/types.h" START_NAMESPACE_STIR @@ -64,41 +60,41 @@ class CListEventPETSIRD : public CListEvent //! Override the default implementation inline void get_bin(Bin& bin, const ProjDataInfo& proj_data_info) const override; - inline void set_map_sptr(shared_ptr new_map_sptr) { map_sptr = new_map_sptr;} - - inline void set_petsird_to_stir_map(shared_ptr new_map) { petsird_to_stir = new_map; } + inline void set_map_sptr(shared_ptr new_map_sptr) { map_sptr = new_map_sptr; } - inline void set_scanner_sptr(shared_ptr new_scanner_sptr) { scanner_sptr = new_scanner_sptr; } + inline void set_petsird_to_stir_map(shared_ptr new_map) { petsird_to_stir = new_map; } inline bool is_valid_template(const ProjDataInfo&) const override { return true; } - inline bool is_prompt() const override { return _prompt; } + inline bool is_prompt() const override { return m_prompt; } inline Succeeded set_prompt(const bool prompt) override { - _prompt = prompt; + m_prompt = prompt; return Succeeded::yes; } inline void set_expanded_detection_bins(const petsird_helpers::ExpandedDetectionBin& det0, - const petsird_helpers::ExpandedDetectionBin& det1) + const petsird_helpers::ExpandedDetectionBin& det1, + const uint32_t tof_idx) { exp_det_0 = det0; exp_det_1 = det1; - } - - inline void set_tof_bin(const uint32_t value) { tof_bin = value; } + m_tof_bin = tof_idx; + } - inline stir::DetectionPosition<> get_stir_det_pos_from_PETSIRD_id(const petsird_helpers::ExpandedDetectionBin& exp_det_bin) const; + inline void set_tof_bin(const uint32_t value) { m_tof_bin = value; } - shared_ptr petsird_to_stir; + inline stir::DetectionPosition<> + get_stir_det_pos_from_PETSIRD_id(const petsird_helpers::ExpandedDetectionBin& exp_det_bin) const; private: - shared_ptr map_sptr; - shared_ptr scanner_sptr; - bool _prompt; + shared_ptr map_sptr = nullptr; + shared_ptr petsird_to_stir = nullptr; + + bool m_prompt; petsird_helpers::ExpandedDetectionBin exp_det_0, exp_det_1; - uint32_t tof_bin; + uint32_t m_tof_bin; }; class CListTimePETSIRD : public ListTime @@ -117,11 +113,8 @@ class CListTimePETSIRD : public ListTime class CListRecordPETSIRD : public CListRecord { public: - CListRecordPETSIRD(shared_ptr scanner_info, - shared_ptr scanner_sptr) - : scanner_info(scanner_info) + CListRecordPETSIRD() { - event_data.set_scanner_sptr(scanner_sptr); } // ~CListRecordPETSIRD() override {} @@ -141,28 +134,19 @@ class CListRecordPETSIRD : public CListRecord // return dynamic_cast(&e2) != 0 && raw == static_cast(e2).r; } - virtual Succeeded init_from_data(const petsird::CoincidenceEvent& data, bool is_prompt = true) + virtual Succeeded init_from_data(const petsird_helpers::ExpandedDetectionBin& det0, + const petsird_helpers::ExpandedDetectionBin& det1, + const uint32_t tof_idx, + const bool is_prompt = true) { - - event_data.set_expanded_detection_bins( - petsird_helpers::expand_detection_bin(*scanner_info, - 0, // TODO type_of_module, currently we only support single module types. - data.detection_bins[0]), - petsird_helpers::expand_detection_bin(*scanner_info, - 0, // TODO type_of_module, currently we only support single module types. - data.detection_bins[1]) - ); - + event_data.set_expanded_detection_bins(det0, det1, tof_idx); event_data.set_prompt(is_prompt); - event_data.set_tof_bin(data.tof_idx); return Succeeded::yes; } private: CListEventPETSIRD event_data; CListTimePETSIRD time_data; - - shared_ptr scanner_info; }; END_NAMESPACE_STIR diff --git a/src/include/stir/listmode/CListRecordPETSIRD.inl b/src/include/stir/listmode/CListRecordPETSIRD.inl index 8059907a35..cfa43e68b8 100644 --- a/src/include/stir/listmode/CListRecordPETSIRD.inl +++ b/src/include/stir/listmode/CListRecordPETSIRD.inl @@ -1,4 +1,4 @@ -/* CListRecordPETSIRD.inl +/* CListRecordPETSIRD.inl Coincidence Event Class for PETSIRD: Inline File Copyright 2025, UMCG @@ -83,14 +83,19 @@ CListEventPETSIRD::get_bin(Bin& bin, const ProjDataInfo& proj_data_info) const DetectionPositionPair<> det_pos_pair; - if (scanner_sptr->get_scanner_geometry() == "Cylindrical") + if (proj_data_info.get_scanner_sptr()->get_scanner_geometry() == "Cylindrical") { + if (!petsird_to_stir) + { + error("CListEventPETSIRD::get_bin: petsird_to_stir map not set. Probably your ProjDataInfo point to a Generic Scanner. \n The Scanner in the Listmode data and the one in the ProjDataInfo must match."); + } + det_pos_pair.pos1() = get_stir_det_pos_from_PETSIRD_id(exp_det_1); det_pos_pair.pos2() = get_stir_det_pos_from_PETSIRD_id(exp_det_0); - det_pos_pair.timing_pos() = static_cast(tof_bin) + (proj_data_info.get_min_tof_pos_num() ); + det_pos_pair.timing_pos() = static_cast(m_tof_bin) + (proj_data_info.get_min_tof_pos_num() ); dynamic_cast(proj_data_info).get_bin_for_det_pos_pair(bin, det_pos_pair); } - else if (scanner_sptr->get_scanner_geometry() == "Generic") + else if (proj_data_info.get_scanner_sptr()->get_scanner_geometry() == "Generic") { // if (!map_sptr) // { @@ -116,16 +121,20 @@ CListEventPETSIRD::get_bin(Bin& bin, const ProjDataInfo& proj_data_info) const // std::endl; const LORAs2Points lor(c1, c2); bin = proj_data_info.get_bin(lor); // } } - else if (scanner_sptr->get_scanner_geometry() == "BlocksOnCylindrical") + else if (proj_data_info.get_scanner_sptr()->get_scanner_geometry() == "BlocksOnCylindrical") { - det_pos_pair.pos1() = (*petsird_to_stir)[exp_det_0]; - det_pos_pair.pos2() = (*petsird_to_stir)[exp_det_1]; + if (!petsird_to_stir) + { + error("CListEventPETSIRD::get_bin: petsird_to_stir map not set. Probably your ProjDataInfo point to a Generic Scanner. \n The Scanner in the Listmode data and the one in the ProjDataInfo must match."); + } + det_pos_pair.pos1() = (*petsird_to_stir)[exp_det_1]; + det_pos_pair.pos2() = (*petsird_to_stir)[exp_det_0]; dynamic_cast(proj_data_info).get_bin_for_det_pos_pair(bin, det_pos_pair); } else { error("CListEventPETSIRD::get_bin: How did I get with an unsupported scanner geometry ? -", - scanner_sptr->get_scanner_geometry()); + proj_data_info.get_scanner_sptr()->get_scanner_geometry()); } } diff --git a/src/include/stir/recon_buildblock/BinNormalisationFromPETSIRD.h b/src/include/stir/recon_buildblock/BinNormalisationFromPETSIRD.h new file mode 100644 index 0000000000..5b594f2dfd --- /dev/null +++ b/src/include/stir/recon_buildblock/BinNormalisationFromPETSIRD.h @@ -0,0 +1,67 @@ +// // +// // +// /*! +// \file +// \ingroup normalisation + +// \brief Declaration of class stir::BinNormalisationFromPETSIRD + +// \author Nikos Efthimiou +// */ +// /* +// Copyright (C) 2025, UMCG +// This file is part of STIR. + +// SPDX-License-Identifier: Apache-2.0 + +// See STIR/LICENSE.txt for details +// */ + + +// #ifndef __stir_recon_buildblock_BinNormalisationFromPETSIRD_H__ +// #define __stir_recon_buildblock_BinNormalisationFromPETSIRD_H__ + +// #include "stir/recon_buildblock/BinNormalisation.h" +// #include "stir/recon_buildblock/BinNormalisationWithCalibration.h" +// #include "stir/RegisteredParsingObject.h" +// #include "stir/ProjData.h" +// #include "stir/shared_ptr.h" + +// using std::string; + +// START_NAMESPACE_STIR + +// class BinNormalisationFromPETSIRD : public RegisteredParsingObject +// { +// private: +// using base_type = BinNormalisation; + +// public: +// //! Name which will be used when parsing a BinNormalisation object +// static const char* const registered_name; + +// BinNormalisationFromPETSIRD(); + +// BinNormalisationFromPETSIRD(const std::string& filename); + +// Succeeded set_up(const shared_ptr& exam_info_sptr, const shared_ptr&) override; + + +// private: + +// void set_defaults() override; + +// void initialise_keymap() override; + +// bool post_processing() override; + +// void read_norm_data(const string& filename); + +// string normalisation_filename; + +// }; + + +// END_NAMESPACE_STIR + +// #endif diff --git a/src/listmode_buildblock/CListModeDataPETSIRD.cxx b/src/listmode_buildblock/CListModeDataPETSIRD.cxx index f118752d44..4fe420d463 100644 --- a/src/listmode_buildblock/CListModeDataPETSIRD.cxx +++ b/src/listmode_buildblock/CListModeDataPETSIRD.cxx @@ -44,669 +44,6 @@ Coincidence LM Data Class for PETSIRD: Implementation START_NAMESPACE_STIR -/*! - \namespace matrix - \brief Lightweight 3x3 matrix and 3D vector helpers used during PETSIRD geometry analysis. - - Provides utilities to: - - transpose a 3x3 matrix, - - subtract two 3x3 matrices, - - extract a rotation axis vector from the skew-symmetric part of a matrix. - - \details - - Mat3: std::array,3> for compact fixed-size storage. - - Vec3: std::array for simple 3D vectors. - - getAxisFromSkew(): - Given S = R - R^T (skew-symmetric part of a rotation matrix R), - returns the axis proportional to: - (S_z,y - S_y,z)/2, (S_x,z - S_z,x)/2, (S_y,x - S_x,y)/2. - For a pure rotation, S encodes the axis direction. - - These helpers assume small numerical noise; thresholds are handled by callers. - - No external dependencies; intended for quick geometric inference (e.g., rotation axis detection). -*/ -namespace matrix -{ - -using Mat3 = std::array, 3>; -using Vec3 = std::array; - -/*! - \brief Transpose a 3x3 matrix. - \param mat Input matrix. - \return Transposed matrix. -*/ -inline Mat3 -transpose(const Mat3& mat) -{ - std::array, 3> result{}; - for (int i = 0; i < 3; ++i) - for (int j = 0; j < 3; ++j) - result[j][i] = mat[i][j]; - return result; -} - -/*! - \brief Subtract two 3x3 matrices (A - B). - \param A Left-hand matrix. - \param B Right-hand matrix. - \return Result of A - B. -*/ -inline Mat3 -subtract(const Mat3& A, const Mat3& B) -{ - std::array, 3> result{}; - for (size_t i = 0; i < 3; ++i) - for (size_t j = 0; j < 3; ++j) - result[i][j] = A[i][j] - B[i][j]; - return result; -} - -/*! - \brief Extract rotation axis from the skew-symmetric matrix S = R - R^T. - \param S Skew-symmetric matrix. - \return Axis vector proportional to the rotation axis. -*/ -inline Vec3 -getAxisFromSkew(const Mat3& S) -{ - return { - 0.5f * (S[2][1] - S[1][2]), // x - 0.5f * (S[0][2] - S[2][0]), // y - 0.5f * (S[1][0] - S[0][1]) // z - }; -} - -} // namespace matrix - -/*! - \namespace vector_utils - \brief Helpers for spacing analysis and axis inference from coordinate sets. - - \details - - \ref vector_utils::get_spacing_uniform computes successive spacings between - sorted unique values and tests if they are uniform within a tolerance. - - \ref vector_utils::getLargestVector returns the largest of three coordinate - sets and logs which axis is inferred as “axial”. -*/ -namespace vector_utils -{ -/*! - \brief Compute spacings between sorted unique values and test uniformity. - \param spacing Output vector. Appends |x[i] - x[i-1]| for i=1..N-1, where x is the sorted version of \a unsorted_block_poss. - \param unsorted_block_poss Set of unique positions (e.g., angles or translations). - \param epsilon Tolerance for uniformity check (default 1e-4). - \return True if all spacings differ from the first spacing by <= \a epsilon, false otherwise. - - \details - - If \a spacing ends up empty (e.g., input size < 2), returns true (trivially uniform). -*/ -inline bool -get_spacing_uniform(std::vector& spacing, const std::set& unsorted_block_poss, double epsilon = 1e-4) -{ - std::vector sorted_z(unsorted_block_poss.begin(), unsorted_block_poss.end()); - for (size_t i = 1; i < sorted_z.size(); ++i) - { - spacing.push_back(std::abs(sorted_z[i] - sorted_z[i - 1])); - } - - return std::all_of(spacing.begin(), spacing.end(), [&](float s) { return std::abs(s - spacing.front()) <= epsilon; }); -} - -/*! - \brief Return the largest of three sets and report inferred axial direction. - \param x Values along X. - \param y Values along Y. - \param z Values along Z. - \return Const reference to the largest set among \a x, \a y, \a z. - - \details - Logs the index of the inferred axial direction: 0 (x), 1 (y), or 2 (z). -*/ -const std::set& -getLargestVector(const std::set& x, const std::set& y, const std::set& z) -{ - const std::set* largest = &x; - int axis = 0; - if (y.size() > largest->size()) - { - largest = &y; - axis = 1; - } - else if (z.size() > largest->size()) - { - largest = &z; - axis = 2; - } - - info(format("I believe the axial direction is the {}.", axis)); - return *largest; -} - -/*! - \brief Collect unique values from a 1D vector. - \param values Output set for unique values. - \param input Input vector. -*/ -void -find_unique_values_1D(std::set& values, const std::vector& input) -{ - for (float val : input) - { - // std::cout << val << std::endl; - values.insert(val); - } -} - -/*! - \brief Collect unique values from a 2D vector (matrix). - \param values Output set for unique values. - \param input Input 2D vector [rows][cols]. -*/ -void -find_unique_values_2D(std::set& values, const std::vector>& input) -{ - for (size_t row = 0; row < input.size(); ++row) - for (size_t col = 0; col < input[row].size(); ++col) - values.insert(input[row][col]); -} - -} // namespace vector_utils - -/*! - \brief Infer scanner blocks and rotation axis from PETSIRD replicated modules. - \param unique_dim1_values Output set of unique translations along X. - \param unique_dim2_values Output set of unique translations along Y. - \param unique_dim3_values Output set of unique translations along Z. - \param replicated_module_list PETSIRD replicated detector modules. - \return Index of rotation axis (0=x, 1=y, 2=z) or -1 if not found. - - \details - - Extracts translation components into the provided sets. - - Uses skew-symmetric part of rotation matrices to infer axis direction. - - Emits warnings for mixed-axis rotations or inconsistencies. -*/ -int -CListModeDataPETSIRD::figure_out_scanner_blocks_and_rotation_axis( - std::set& unique_dim1_values, - std::set& unique_dim2_values, - std::set& unique_dim3_values, - const std::vector& replicated_module_list) -{ - auto insertTranslations = [&](const petsird::RigidTransformation& trans) { - unique_dim1_values.insert(trans.matrix.at(0, 3)); - unique_dim2_values.insert(trans.matrix.at(1, 3)); - unique_dim3_values.insert(trans.matrix.at(2, 3)); - }; - - auto extractRotationMatrix = [](const petsird::RigidTransformation& trans) -> matrix::Mat3 { - matrix::Mat3 R; - for (int i = 0; i < 3; ++i) - for (int j = 0; j < 3; ++j) - R[i][j] = trans.matrix.at(i, j); - return R; - // skew = matrix::subtract(R, matrix::transpose(R)); - // auto rot = matrix::getAxisFromSkew(skew); - }; - - std::array, 3> skew; - int detected_axis = -1; - - for (const auto& module : replicated_module_list) - for (const auto& mod_trans : module.transforms) - { - insertTranslations(mod_trans); - matrix::Mat3 R = extractRotationMatrix(mod_trans); - skew = matrix::subtract(R, matrix::transpose(R)); - auto axis_vec = matrix::getAxisFromSkew(skew); - - int current_axis = -1; - for (int i = 0; i < 3; ++i) - { - if (std::abs(axis_vec[i]) > 1e-6f) - { - if (current_axis != -1) - { - warning("Rotation involves multiple axis components. Possibly non-pure rotation."); - current_axis = -2; // Sentinel for mixed axes - return -1; - } - current_axis = i; - } - } - - if (current_axis >= 0) - { - if (detected_axis == -1) - detected_axis = current_axis; - else if (detected_axis != current_axis) - warning("Inconsistent rotation axis detected between modules."); - } - } - info(format("Rotation axis of blocks inferred as axis index {}", detected_axis)); - return detected_axis; -} - -/*! - \brief Determine block element translations and radius. - \param unique_dim1_values Output set of element translations along X. - \param unique_dim2_values Output set of element translations along Y. - \param unique_dim3_values Output set of element translations along Z. - \param radius Output inferred radius (positive component orthogonal to rotation axis). - \param radius_index Output index of the axis containing the radius component. - \param rotation_axis Known rotation axis (0=x, 1=y, 2=z). - \param replicated_module_list PETSIRD replicated detector modules. - - \details - - Scans element-level transforms to infer radius and collect translations. - - Emits warnings if mixed radii are detected. -*/ -void -CListModeDataPETSIRD::figure_out_block_element_transformations( - std::set& unique_dim1_values, - std::set& unique_dim2_values, - std::set& unique_dim3_values, - float& radius, - int& radius_index, - const int rotation_axis, - const std::vector& replicated_module_list) -{ - - auto insert_translation = [&](const petsird::RigidTransformation& trans) { - unique_dim1_values.insert(trans.matrix.at(0, 3)); - unique_dim2_values.insert(trans.matrix.at(1, 3)); - unique_dim3_values.insert(trans.matrix.at(2, 3)); - }; - - auto detect_radius = [&](const petsird::RigidTransformation& trans) -> bool { - for (int i = 0; i < 3; ++i) - { - if (i == rotation_axis) - continue; - float candidate = trans.matrix.at(i, 3); - if (candidate > 0.0f) - { - radius = candidate; - radius_index = i; - return true; - } - } - return false; - }; - - for (const auto& module : replicated_module_list) - { - for (const auto& el_trans : module.object.detecting_elements.transforms) - { - if (radius == 0.0f) - { - if (!detect_radius(el_trans)) - { - error("Unable to determine radius from translation components."); - continue; - } - } - else - { - float current = el_trans.matrix.at(radius_index, 3); - if (std::abs(current - radius) > 1e-4f) - warning("Mixed radii detected. Consider checking for misaligned modules."); - } - - insert_translation(el_trans); - // std::cout << el_trans.matrix << std::endl; - } - } -} - -void -CListModeDataPETSIRD::figure_out_block_angles(std::set& unique_angle_modules, - const int rot_axis, - const std::vector& replicated_module_list) -{ - for (const auto& module : replicated_module_list) - for (const auto& transform : module.transforms) - { - if (rot_axis == 0) - unique_angle_modules.insert( - std::fabs(int(1000.F * std::atan2(transform.matrix.at(1, 0), transform.matrix.at(2, 0))) / 1000.F)); - else if (rot_axis == 1) - unique_angle_modules.insert( - std::fabs(int(1000.F * std::atan2(transform.matrix.at(2, 0), transform.matrix.at(0, 0))) / 1000.F)); - else if (rot_axis == 2) - unique_angle_modules.insert( - std::fabs(int(1000.F * std::atan2(transform.matrix.at(1, 0), transform.matrix.at(0, 0))) / 1000.F)); - } -} - -bool -almostEqual(double a, double b, double tol = 1e-6) -{ - return std::fabs(a - b) <= tol; -} - -// Detect groupSize along dim2 (y) and loops along dim3 (z) -// Returns true on success, false if pattern doesn't match the assumed structure. -bool -inferGroupSizes_dim2_dim3(const std::vector>& pts, - std::size_t& groupSize_dim2, - std::size_t& groupSize_dim3, - float tol = 1e-5f) -{ - const std::size_t n = pts.size(); - groupSize_dim2 = groupSize_dim3 = 0; - - if (n == 0) - return false; - - if (n == 1) - { - groupSize_dim2 = 1; - groupSize_dim3 = 1; - return true; - } - - // STIR CartesianCoordinate3D is (z, y, x) - const float x0 = pts[0].x(); - const float z0 = pts[0].z(); - - // 1) Find how many initial points keep x and z the same - std::size_t runLen = 1; - while (runLen < n && almostEqual(pts[runLen].x(), x0, tol) && almostEqual(pts[runLen].z(), z0, tol)) - { - ++runLen; - } - - groupSize_dim2 = runLen; - - // Must tile the full array - if (groupSize_dim2 == 0 || n % groupSize_dim2 != 0) - return false; - - groupSize_dim3 = n / groupSize_dim2; - - // 2) Check each block of size groupSize_dim2 has constant x,z - for (std::size_t b = 0; b < groupSize_dim3; ++b) - { - std::size_t start = b * groupSize_dim2; - float xb = pts[start].x(); - float zb = pts[start].z(); - - for (std::size_t i = 1; i < groupSize_dim2; ++i) - { - const auto& p = pts[start + i]; - if (!almostEqual(p.x(), xb, tol) || !almostEqual(p.z(), zb, tol)) - { - return false; // pattern breaks inside a block - } - } - } - - // 3) Optionally check that z actually changes between blocks - for (std::size_t b = 1; b < groupSize_dim3; ++b) - { - float z_prev = pts[(b - 1) * groupSize_dim2].z(); - float z_curr = pts[b * groupSize_dim2].z(); - if (almostEqual(z_prev, z_curr, tol)) - { - return false; // outer loop didn't move in z - } - } - - return true; -} - -bool -CListModeDataPETSIRD::isCylindricalConfiguration(const std::vector& replicated_module_list) -{ - // Determine DOI based on material - float average_doi = 0.0; - if (scanner_info->bulk_materials.size() > 0) - { - const std::string& material = scanner_info->bulk_materials[0].name; - if (material.size() > 0) - average_doi = (material == "BGO") ? 5.0f : (material == "LSO" || material == "LYSO") ? 7.0f : 0.0f; - } - - const petsird::TypeOfModule type_of_module = replicated_module_list.size(); - - if (type_of_module > 1) - { - info("Multiple types of PETSIRD modules are not supported. Abord."); - return false; - } - - const auto& tof_bin_edges = scanner_info->tof_bin_edges[type_of_module - 1][type_of_module - 1]; - std::cout << "Num. of TOF bins " << tof_bin_edges.NumberOfBins() << std::endl; - - std::set unique_tof_values; - vector_utils::find_unique_values_2D(unique_tof_values, scanner_info->tof_resolution); - - numberOfModules = replicated_module_list[0].NumberOfObjects(); - numberOfElementsIndices = replicated_module_list[0].object.detecting_elements.NumberOfObjects(); - std::set unique_dim1_values, unique_dim2_values, unique_dim3_values; - std::set unique_tof_resolutions; - - const int rotation_axis = figure_out_scanner_blocks_and_rotation_axis( - unique_dim1_values, unique_dim2_values, unique_dim3_values, replicated_module_list); - if (rotation_axis == -1) - return false; - - const std::set& main_axis = vector_utils::getLargestVector(unique_dim1_values, unique_dim2_values, unique_dim3_values); - - int num_transaxial_blocks = numberOfModules / main_axis.size(); - info(format("I deduce that the scanner has {} transaxial number of blocks", num_transaxial_blocks)); - - float radius = 0; - int radius_indx = -1; - - std::set unique_elements_dim1_values, unique_elements_dim2_values, unique_elements_dim3_values; - figure_out_block_element_transformations(unique_elements_dim1_values, - unique_elements_dim2_values, - unique_elements_dim3_values, - radius, - radius_indx, - rotation_axis, - replicated_module_list); - std::set unique_angle_modules; - figure_out_block_angles(unique_angle_modules, rotation_axis, replicated_module_list); - - std::vector block_angular_spacing; - if (!vector_utils::get_spacing_uniform( - block_angular_spacing, unique_angle_modules, 1e-2)) /// epsilon * 10000) // relax epsilon here - return false; - - std::size_t group2 = 0, group3 = 0; - { - std::vector> pet_sird_positions; - const petsird::TypeOfModule type_of_module = replicated_module_list.size() - 1; - for (uint32_t module = 0; module < 1; module++) - { - for (uint32_t elem = 0; elem < numberOfElementsIndices; elem++) - { - petsird::ExpandedDetectionBin expanded_detection_bin{ module, elem, 1 }; - auto box_shape = petsird_helpers::geometry::get_detecting_box(*scanner_info, type_of_module, expanded_detection_bin); - CartesianCoordinate3D mean_coord; - for (auto& corner : box_shape.corners) - { // if STIR (z,y,x) -> PETSIRD (-y, -x, z) pheraps the order below needs to be changed - mean_coord.x() = +corner.c[0] / box_shape.corners.size(); - mean_coord.y() = +corner.c[1] / box_shape.corners.size(); - mean_coord.z() = +corner.c[2] / box_shape.corners.size(); - } - // mean_coord.z() += this_scanner_sptr->get_axial_crystal_spacing() / - // save mean pos into map - pet_sird_positions.push_back(mean_coord); - } - } - - std::cout << "Nikos here" << std::endl; - - if (inferGroupSizes_dim2_dim3(pet_sird_positions, group2, group3)) - { - std::cout << "groupSize_dim2 = " << group2 << "\n"; - - std::cout << "groupSize_dim3 = " << group3 << "\n"; - } - else - { - std::cout << "No (dim2, dim3) loop structure detected.\n"; - group2 = 1; - group3 = 1; - } - std::cerr << "Nikos here" << std::endl; - } - - std::vector element_horizontal_spacing, element_vertical_spacing; - std::set unique_elements_horizontal_values, unique_elements_vertical_values; - if (radius_indx == 0) - { - if (!vector_utils::get_spacing_uniform(element_horizontal_spacing, unique_elements_dim3_values)) - return false; - if (!vector_utils::get_spacing_uniform(element_vertical_spacing, unique_elements_dim2_values)) - return false; - unique_elements_horizontal_values = unique_elements_dim3_values; - unique_elements_vertical_values = unique_elements_dim2_values; - } - else - { - error("TODO!"); - } -{ - for (size_t i = 0; i < tof_bin_edges.NumberOfBins(); ++i) - { - std::cout << "TOF bin edge " << i << ": " << tof_bin_edges.edges[i] << "\n"; - } -} - blocks_per_bucket_transaxial = group2 > 1 ? unique_elements_vertical_values.size() / group2 : group2; - std::cout << "blocks per bucket in transaxial direction = " << blocks_per_bucket_transaxial << "\n"; - blocks_per_bucket_axial = group3 > 1 ? unique_elements_horizontal_values.size() / (numberOfElementsIndices / group3) : group3; - std::cout << "blocks per bucket in axial direction = " << blocks_per_bucket_axial << "\n"; - num_axial_crystals_per_block = unique_elements_horizontal_values.size() / blocks_per_bucket_axial; - num_trans_crystals_per_block = unique_elements_vertical_values.size() / blocks_per_bucket_transaxial; - - std::vector block_axial_spacing; - vector_utils::get_spacing_uniform(block_axial_spacing, main_axis); - if (block_axial_spacing.size() < 1) - { - // std::set::iterator it = unique_elements_horizontal_values.begin(); - // std::advance(it,0); - float begin = *std::next(unique_elements_horizontal_values.begin(), 0); - // std::advance(it,unique_elements_horizontal_values.size()-1); - float end = *std::next(unique_elements_horizontal_values.begin(), unique_elements_horizontal_values.size() - 1); - block_axial_spacing.push_back(std::abs(end - begin)); - } - - info(format("I counted {} axial blocks with spacing {}", unique_dim3_values.size(), block_axial_spacing[0])); - - // Check if the cyrcle area is less than 5% different from the polygon - float expected_circle_area = float(M_PI * radius * radius); - ; - float polygon_area - = float(0.5f * unique_angle_modules.size() * radius * radius * std::sin(2.f * float(M_PI) / unique_angle_modules.size())); - info(format("Circle area: {}, Polygon area: {}, pct {}", - expected_circle_area, - polygon_area, - std::abs(expected_circle_area - polygon_area) / expected_circle_area)); - if (std::abs(expected_circle_area - polygon_area) / expected_circle_area < 0.1f) - { - info("the cylindical area is more then 95% matching the polygon area. We will predsume a cylindrical configuration."); - this_scanner_sptr.reset(new Scanner(Scanner::User_defined_scanner, - std::string("PETSIRD_defined_scanner"), - /* num dets per ring */ - (num_transaxial_blocks * unique_elements_vertical_values.size()), - unique_dim3_values.size() * unique_elements_horizontal_values.size() /* num of rings */, - /* number of non arccor bins */ - (num_transaxial_blocks * unique_elements_vertical_values.size()) / 2, - /* number of maximum arccor bins */ - (num_transaxial_blocks * unique_elements_vertical_values.size()) / 2, - /* inner ring radius */ - radius, - /* doi */ average_doi, // average_doi, - /* ring spacing */ - element_horizontal_spacing[0], //* 10.f, - // bin_size_v - element_vertical_spacing[0], // * 10.f, - /*intrinsic_tilt_v*/ - 0.f, - /*num_axial_blocks_per_bucket_v */ - blocks_per_bucket_axial, - /*num_transaxial_blocks_per_bucket_v*/ - blocks_per_bucket_transaxial, - /*num_axial_crystals_per_block_v*/ - num_axial_crystals_per_block, - /*num_transaxial_crystals_per_block_v*/ - num_trans_crystals_per_block, - /*num_axial_crystals_per_singles_unit_v*/ - unique_elements_horizontal_values.size() / blocks_per_bucket_axial, - /*num_transaxial_crystals_per_singles_unit_v*/ - unique_elements_vertical_values.size() / blocks_per_bucket_transaxial, - /*num_detector_layers_v*/ - 1, // num_detector_layers_v - scanner_info->energy_resolution_at_511.front(), // energy_resolution_v - 511, // reference_energy_v - tof_bin_edges.NumberOfBins(), - (tof_bin_edges.edges[1] - tof_bin_edges.edges[0])/speed_of_light_in_mm_per_ps_div2, - *unique_tof_values.begin() * 10 // non-TOF - )); - return true; - } - else - { - info("the cylindical area is less then 95% matching the polygon area. We will predsume a non-cylindrical configuration."); - this_scanner_sptr.reset( - new Scanner(Scanner::User_defined_scanner, - std::string("PETSIRD_defined_scanner"), - /* num dets per ring */ - (num_transaxial_blocks * unique_elements_vertical_values.size()), - unique_dim3_values.size() * unique_elements_horizontal_values.size() /* num of rings */, - /* number of non arccor bins */ - (num_transaxial_blocks * unique_elements_vertical_values.size()) / 2, - /* number of maximum arccor bins */ - (num_transaxial_blocks * unique_elements_vertical_values.size()) / 2, - /* inner ring radius */ - radius, - /* doi */ average_doi, - /* ring spacing */ - element_horizontal_spacing[0], //* 10.f, - // bin_size_v - element_vertical_spacing[0], // * 10.f, - /*intrinsic_tilt_v*/ - 0.f, - /*num_axial_blocks_per_bucket_v */ - 1, - /*num_transaxial_blocks_per_bucket_v*/ - 1, - /*num_axial_crystals_per_block_v*/ - blocks_per_bucket_axial * num_axial_crystals_per_block, - /*num_transaxial_crystals_per_block_v*/ - blocks_per_bucket_transaxial * num_trans_crystals_per_block, - /*num_axial_crystals_per_singles_unit_v*/ - unique_elements_horizontal_values.size() / blocks_per_bucket_axial, - /*num_transaxial_crystals_per_singles_unit_v*/ - unique_elements_vertical_values.size() / blocks_per_bucket_transaxial, - /*num_detector_layers_v*/ - 1, // num_detector_layers_v - scanner_info->energy_resolution_at_511.front(), // energy_resolution_v - 511, // reference_energy_v - 1, - 0.F, - 0.F, // non-TOF - "BlocksOnCylindrical", // scanner_geometry_v - (*std::next(unique_elements_horizontal_values.begin()) - - *unique_elements_horizontal_values.begin()), // axial_crystal_spacing_v - (*std::next(unique_elements_vertical_values.begin()) - - *unique_elements_vertical_values.begin()), // transaxial_crystal_spacing_v - (*std::next(unique_elements_horizontal_values.begin()) - *unique_elements_horizontal_values.begin()) - * num_axial_crystals_per_block * blocks_per_bucket_axial, // axial_block_spacing_v - (*std::next(unique_elements_vertical_values.begin()) - *unique_elements_vertical_values.begin()) - * num_trans_crystals_per_block * blocks_per_bucket_transaxial, // transaxial_block_spacing_v - "" // crystal_map_file_name_v - )); - return false; - } - - return true; -} - CListModeDataPETSIRD::CListModeDataPETSIRD(const std::string& listmode_filename, bool use_hdf5) : use_hdf5(use_hdf5) { @@ -722,8 +59,7 @@ CListModeDataPETSIRD::CListModeDataPETSIRD(const std::string& listmode_filename, current_lm_data_ptr->ReadHeader(header); scanner_info = std::make_shared(header.scanner); - petsird::ScannerGeometry scanner_geo = scanner_info->scanner_geometry; - std::vector replicated_module_list = scanner_geo.replicated_modules; + // std::vector replicated_module_list = scanner_info->scanner_geometry.replicated_modules; // Get the first TimeBlock // if ( @@ -736,159 +72,19 @@ CListModeDataPETSIRD::CListModeDataPETSIRD(const std::string& listmode_filename, else error("CListModeDataPETSIRD: holds_alternative not true. Abord."); - bool is_cylindrical = isCylindricalConfiguration(replicated_module_list); - // if (is_cylindrical) - // { - // int tof_mash_factor = 1; - // this->set_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())); - // } - // else - // { - DetectorCoordinateMap::det_pos_to_coord_type petsird_map; - const petsird::TypeOfModule type_of_module = replicated_module_list.size() - 1; - - petsird_to_stir = std::make_shared(); - - enum class InnerLoopDim - { - Axial, - Tangential, - Radial - }; - InnerLoopDim inner_dim = InnerLoopDim::Tangential; // determined from your groupSize analysis - - // PRECOMPUTED from previous step: - std::size_t groupSize - = blocks_per_bucket_transaxial == 1 ? 0 : num_trans_crystals_per_block; // e.g. 5, or 1 if purely monotonic - // extern InnerLoopDim inner_dim; // Axial / Tangential / Radial - - const int num_ax = blocks_per_bucket_axial * num_axial_crystals_per_block; - const int num_tang = blocks_per_bucket_transaxial * num_trans_crystals_per_block; - - int ind = 0; - - for (uint32_t module = 0; module < numberOfModules; module++) - for (uint32_t elem = 0; elem < numberOfElementsIndices; elem++) - // for (uint32_t ener = 0; ener < num_event_energy_bins; ener++) //energy not supported yet - { - // ---- 1) Decompose elem into: tile, in-tile indices ---- - const uint32_t tileSize = groupSize * groupSize; // elems per tile - const uint32_t tiles_per_bucket = blocks_per_bucket_axial * blocks_per_bucket_transaxial; - - const uint32_t tile = (groupSize > 0 ? elem / tileSize : 0); // which tile - const uint32_t inTile = (groupSize > 0 ? elem % tileSize : elem); // index inside tile - - const uint32_t i0 = inTile % groupSize; // fast inside tile (local "x") - const uint32_t i1 = inTile / groupSize; // slow inside tile (local "y") - - int ax_pos = 0; - int tang_pos = 0; - int rad_pos = 0; // ignored for now - - // ---- 2) Decode which block (tile) we are in along axial/tangential ---- - switch (inner_dim) - { - case InnerLoopDim::Tangential: { - // Here we assume: - // - i0 runs tangential inside a block - // - i1 runs axial inside a block - // - // tiles are laid out as: - // tangential: blocks_per_bucket_transaxial tiles - // axial: blocks_per_bucket_axial tiles - - const uint32_t tang_block = tile % blocks_per_bucket_transaxial; - const uint32_t axial_block = tile / blocks_per_bucket_transaxial; - - tang_pos = static_cast(tang_block * groupSize + i0); - ax_pos = static_cast(axial_block * groupSize + i1); - break; - } - - case InnerLoopDim::Axial: { - // Here we assume: - // - i0 runs axial inside a block - // - i1 runs tangential inside a block - // - // tiles are laid out as: - // axial: blocks_per_bucket_axial tiles - // tangential: blocks_per_bucket_transaxial tiles - - const uint32_t axial_block = tile % blocks_per_bucket_axial; - const uint32_t tang_block = tile / blocks_per_bucket_axial; - - ax_pos = static_cast(axial_block * groupSize + i0); - tang_pos = static_cast(tang_block * groupSize + i1); - break; - } - } - - DetectionPosition<> detpos( - tang_pos + module * (num_trans_crystals_per_block * blocks_per_bucket_transaxial), ax_pos, rad_pos); - - petsird::ExpandedDetectionBin expanded_detection_bin{ module, elem, 0 }; - - if (this_scanner_sptr->get_scanner_geometry() == "Generic") - { - auto box_shape = petsird_helpers::geometry::get_detecting_box(*scanner_info, type_of_module, expanded_detection_bin); - CartesianCoordinate3D mean_coord(0.f, 0.f, 0.f); - - for (auto& corner : box_shape.corners) - { - mean_coord.x() += corner.c[0] / box_shape.corners.size(); - mean_coord.y() += corner.c[1] / box_shape.corners.size(); - mean_coord.z() += corner.c[2] / box_shape.corners.size(); - } - - petsird_map[detpos] = mean_coord; - - std::cout << ind << " : " << detpos.radial_coord() << ", " << detpos.axial_coord() << ", " - << detpos.tangential_coord() << ", " << mean_coord.x() << ", " << mean_coord.y() << ", " << mean_coord.z() - << "\n"; - ++ind; - } - else if (this_scanner_sptr->get_scanner_geometry() == "BlocksOnCylindrical" || is_cylindrical) - { - (*petsird_to_stir)[expanded_detection_bin] = detpos; - } - - // auto detectionBin = petsird_helpers::make_detection_bin( - // *scanner_info, - // type_of_module, - // expanded_detection_bin); - - // petsird_map[detectionBin] = mean_coord; - - // Save to shared_ptr map - } - - // // this->map.reset(new DetectorCoordinateMapLightPETSIRD(petsird_map)); - // this->map.reset(new DetectorCoordinateMap(petsird_map)); - // // this_scanner_sptr->get_detector_map_sptr()->set_detector_coordinate_map_light_sptr( - // // std::make_shared(petsird_map)); - // this->map->write_detectormap_to_file("petsird_detector_map_from_scanner_definition.txt"); - // this_scanner_sptr->set_detector_map(petsird_map); - // this_scanner_sptr->set_up(); + petsird_info_sptr = std::make_shared(scanner_info); + auto stir_scanner_sptr = petsird_info_sptr->get_scanner_sptr(); int tof_mash_factor = 1; this->set_proj_data_info_sptr(std::const_pointer_cast( - ProjDataInfo::construct_proj_data_info(this_scanner_sptr, + ProjDataInfo::construct_proj_data_info(petsird_info_sptr->get_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(), + petsird_info_sptr->get_scanner_sptr()->get_num_rings() - 1, + petsird_info_sptr->get_scanner_sptr()->get_num_detectors_per_ring() / 2, + petsird_info_sptr->get_scanner_sptr()->get_max_num_non_arccorrected_bins(), /* arc_correction*/ false, tof_mash_factor) ->create_shared_clone())); - // } shared_ptr _exam_info_sptr(new ExamInfo); // Only PET scanners supported @@ -920,15 +116,17 @@ CListModeDataPETSIRD::open_lm_file() const shared_ptr CListModeDataPETSIRD::get_empty_record_sptr() const { - shared_ptr sptr(new CListRecordPETSIRD(scanner_info, this_scanner_sptr)); //, this->map, petsird_to_stir)); - if (this_scanner_sptr->get_scanner_geometry() == "Generic") + shared_ptr sptr( + new CListRecordPETSIRD()); //, this->map, petsird_to_stir)); + if (petsird_info_sptr->get_scanner_sptr()->get_scanner_geometry() == "Generic") { std::dynamic_pointer_cast(sptr)->event().set_map_sptr(this->map); } - else if (this_scanner_sptr->get_scanner_geometry() == "BlocksOnCylindrical" || - this_scanner_sptr->get_scanner_geometry() == "Cylindrical") + else if (petsird_info_sptr->get_scanner_sptr()->get_scanner_geometry() == "BlocksOnCylindrical" + || petsird_info_sptr->get_scanner_sptr()->get_scanner_geometry() == "Cylindrical") { - std::dynamic_pointer_cast(sptr)->event().set_petsird_to_stir_map(petsird_to_stir); + std::dynamic_pointer_cast(sptr)->event().set_petsird_to_stir_map( + petsird_info_sptr->get_petsird_to_stir_map()); } // std::dynamic_pointer_cast(sptr)->event().set_scanner_sptr( @@ -952,7 +150,17 @@ CListModeDataPETSIRD::get_next_record(CListRecord& record_of_general_type) const if (event_list.size() == 0) return Succeeded::no; - if (record.init_from_data(event_list.at(curr_event_in_event_block), curr_is_prompt) == Succeeded::no + auto event = event_list.at(curr_event_in_event_block); + + if (record.init_from_data( + petsird_helpers::expand_detection_bin(*scanner_info, + 0, // TODO type_of_module, currently we only support single module types. + event.detection_bins[0]), + petsird_helpers::expand_detection_bin(*scanner_info, + 0, // TODO type_of_module, currently we only support single module types. + event.detection_bins[1]), event.tof_idx, + curr_is_prompt) + == Succeeded::no || record_of_general_type.time().set_time_in_millisecs(curr_event_block.time_interval.start) == Succeeded::no) { return Succeeded::no; diff --git a/src/listmode_buildblock/CMakeLists.txt b/src/listmode_buildblock/CMakeLists.txt index 1168a8b514..e5d3dc2b7a 100644 --- a/src/listmode_buildblock/CMakeLists.txt +++ b/src/listmode_buildblock/CMakeLists.txt @@ -72,13 +72,5 @@ if (HAVE_HDF5) endif() if (HAVE_PETSIRD) -target_include_directories(listmode_buildblock - PUBLIC -$ - $ -) -target_link_libraries(listmode_buildblock - PUBLIC - petsird_generated - ) +target_link_libraries(listmode_buildblock PUBLIC petsird_generated) endif() diff --git a/src/recon_buildblock/BinNormalisationFromPETSIRD.cxx b/src/recon_buildblock/BinNormalisationFromPETSIRD.cxx new file mode 100644 index 0000000000..e6799a6d54 --- /dev/null +++ b/src/recon_buildblock/BinNormalisationFromPETSIRD.cxx @@ -0,0 +1,74 @@ +// // +// // +// /* +// Copyright (C) 2025, UMCG +// This file is part of STIR. + +// SPDX-License-Identifier: Apache-2.0 + +// See STIR/LICENSE.txt for details +// */ +// /*! +// \file +// \ingroup normalisation + +// \brief Implementation for class stir::BinNormalisationFromPETSIRD + +// \author Nikos Efthimiou +// */ + +// #include "stir/recon_buildblock/BinNormalisationFromPETSIRD.h" + +// START_NAMESPACE_STIR + +// const char* const BinNormalisationFromPETSIRD::registered_name = "From PETSIRD"; + +// void +// BinNormalisationFromPETSIRD::set_defaults() +// { +// base_type::set_defaults(); +// normalisation_filename = ""; +// } + +// void +// BinNormalisationFromPETSIRD::initialise_keymap() +// { +// base_type::initialise_keymap(); +// parser.add_start_key("Bin Normalisation From PETSIRD"); +// parser.add_key("normalisation_filename", &normalisation_filename); +// parser.add_stop_key("End Bin Normalisation From PETSIRD"); +// } + +// bool +// BinNormalisationFromPETSIRD::post_processing() +// { +// if (base_type::post_processing()) +// return true; +// read_norm_data(normalisation_filename); +// } + +// BinNormalisationFromPETSIRD::BinNormalisationFromPETSIRD() +// { +// set_defaults(); +// } + +// BinNormalisationFromPETSIRD::BinNormalisationFromPETSIRD(const std::string& filename) +// { +// read_norm_data(filename); +// } + +// Succeeded +// BinNormalisationFromPETSIRD::set_up(const shared_ptr& exam_info_sptr, +// const shared_ptr& proj_data_info_ptr_v) +// { +// base_type::set_up(exam_info_sptr, proj_data_info_ptr_v); +// } + +// void +// BinNormalisationFromPETSIRD::read_norm_data(const string& filename) +// { +// normalisation_filename = filename; +// } + + +// END_NAMESPACE_STIR \ No newline at end of file diff --git a/src/recon_buildblock/CMakeLists.txt b/src/recon_buildblock/CMakeLists.txt index 77ba2d0432..3df0af5191 100644 --- a/src/recon_buildblock/CMakeLists.txt +++ b/src/recon_buildblock/CMakeLists.txt @@ -104,6 +104,12 @@ list(APPEND ${dir_LIB_SOURCES} BinNormalisationFromECAT8.cxx ) +# if (HAVE_PETSIRD) +# list(APPEND ${dir_LIB_SOURCES} +# BinNormalisationFromPETSIRD.cxx +# ) +# endif() + if (HAVE_HDF5) list(APPEND ${dir_LIB_SOURCES} BinNormalisationFromGEHDF5.cxx @@ -182,3 +188,15 @@ endif() if (STIR_WITH_CUDA) target_link_libraries(recon_buildblock PRIVATE CUDA::cudart) endif() + +# if (HAVE_PETSIRD) +# target_include_directories(recon_buildblock +# PUBLIC +# $ +# $ +# ) +# target_link_libraries(recon_buildblock +# PUBLIC +# petsird +# ) +# endif() \ No newline at end of file