lm_to_projdata for the SAFIR listmode and with Cylindrical projdata template crashes
But everything works fine with the BlocksOnCylindrical projdata template.

Hi
checking the relevant code: https://github.com/UCL/STIR/blob/2945b72f9974a658f866fc2f47bb07ea869e0182/src/include/stir/listmode/CListRecordSAFIR.inl#L59 and https://github.com/UCL/STIR/blob/2945b72f9974a658f866fc2f47bb07ea869e0182/src/include/stir/listmode/CListRecordSAFIR.h#L105-L106 I think this might be because the cylindrical case still needs a detector-map to be read from file (see #1078 ). Did you pass one?
True! Yes, if I give it the crystal map everything works. Thank you so much, Kris
reopening as not really fixed in the code.
I didn’t write the SAFIR code, but the main difficulty is that it serves 2 purposes:
- It allows reading a listmode file in SAFIR format
- It allows assigning LORs to a “virtual scanner”. (This was implemented before the “blocks” and “generic” scanners were made available).
My impression is that the “virtual scanner” functionality is enabled when there is a crystal map in the *SAFIR.par file: CListEventSAFIR::get_bin() will fall back to finding x,y,z from the crystal map, and then call proj_data_info.get_bin() (with proj_data_info initialised from the template), see here.
The "non-virtual" code path starts with
https://github.com/UCL/STIR/blob/7508babaf5e8147a3563f1642a47301af4c0834d/src/include/stir/listmode/CListRecordSAFIR.inl#L93-L97
as map_sptr==0, and https://github.com/UCL/STIR/blob/7508babaf5e8147a3563f1642a47301af4c0834d/src/include/stir/listmode/CListRecordSAFIR.h#L106
This will boil down to
det_pos_pair.pos1() = scanner_sptr->get_detector_map_sptr()->get_det_pos_for_index(det_pos_pair.pos1());
This crashes in the cylindrical scanner case #1078.
However, it is unclear to me why we use this call to DetectorCoordinateMap::get_det_pos_for_index here at all. There is some documentation here on sorting which applies to the case where we read the crystal map for the scanner from file. It seems unnecessary in the cylindrical or even block case (although in the latter case, it presumably depends on the order used in GeometryBlocksOnCylindrical::build_crystal_maps. Maybe @markusjehl @emikhay or @SomayehSaghamanesh can shed some light on this.
Some ideas therefore
- Current work-around is to use a crystal map in the *SAFIR.par file
- Minimal code change:
use_map()should throw an error ifscanner_sptr->get_detector_map_sptr() == 0 - Make code work for cylindrical: If
map_sptr==0andscanner_sptr->get_detector_map_sptr()==0, skip L96-97. - (more drastic). Remove L96-97 completely, potentially breaking backwards compatibility.
PS: the sorting of the detector-map is quite confusing. Functions in ProjDataInfoGeneric* are written in terms of "det_pos" (indexing into the sorted crystal map). It's only the SAFIR code that uses "index -> sorted" I believe. I'd prefer to cut the sorting out completely...
Using the formula “angle = math.atan2(y_scaled, x_scaled) % (2 * np.pi)”, the XYZ coordinates in list-mode can be mapped to ringA, ringB, detA, and detB, and then stored in the following format. This enables the conversion from list-mode to SAFIR-format, and subsequently to STIR sinograms.
https://github.com/UCL/STIR/blob/master/src/include/stir/listmode/CListRecordSAFIR.h “ /*! 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 */ ” For more details, refer to: SAFIR Listmode Virtual Scanner Example.