STIR icon indicating copy to clipboard operation
STIR copied to clipboard

lm_to_projdata for the SAFIR listmode and with Cylindrical projdata template crashes

Open emikhay opened this issue 3 years ago • 6 comments

But everything works fine with the BlocksOnCylindrical projdata template.

emikhay avatar Oct 06 '22 10:10 emikhay

stir_issue

emikhay avatar Oct 06 '22 10:10 emikhay

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?

KrisThielemans avatar Oct 06 '22 16:10 KrisThielemans

True! Yes, if I give it the crystal map everything works. Thank you so much, Kris

emikhay avatar Oct 10 '22 07:10 emikhay

reopening as not really fixed in the code.

KrisThielemans avatar Jan 14 '25 01:01 KrisThielemans

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

  1. Current work-around is to use a crystal map in the *SAFIR.par file
  2. Minimal code change: use_map() should throw an error if scanner_sptr->get_detector_map_sptr() == 0
  3. Make code work for cylindrical: If map_sptr==0 and scanner_sptr->get_detector_map_sptr()==0, skip L96-97.
  4. (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...

KrisThielemans avatar Jan 14 '25 01:01 KrisThielemans

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.

chengqi001 avatar Jan 14 '25 04:01 chengqi001