Skip to content

Commit 151c457

Browse files
Merge pull request #1319 from KrisThielemans/TOF_segment_and_vis
TOF segment bug fix, extra members and update visualation to add TOF
2 parents 75a9342 + c0c4c5b commit 151c457

File tree

8 files changed

+89
-44
lines changed

8 files changed

+89
-44
lines changed

examples/python/projdata_visualisation/BackendTools/STIRInterface.py

Lines changed: 11 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
# Copyright 2022 University College London
1+
# Copyright 2022, 2024 University College London
22

33
# Author Robert Twyman
44

@@ -23,7 +23,7 @@ class ProjDataDims(Enum):
2323
AXIAL_POS = auto()
2424
VIEW_NUMBER = auto()
2525
TANGENTIAL_POS = auto()
26-
26+
TIMING_POS = auto()
2727

2828
class ProjDataVisualisationBackend:
2929
"""Class used as STIR interface to the projection data for ProjDataVisualisation."""
@@ -84,13 +84,14 @@ def print_segment_data_configuration(self) -> None:
8484
f"\tNumber of axial positions:\t\t\t{self.segment_data.get_num_axial_poss()}\n"
8585
)
8686

87-
def refresh_segment_data(self, segment_number=0) -> stir.FloatSegmentByView:
87+
def refresh_segment_data(self, segment_number=0, timing_pos=0) -> stir.FloatSegmentByView:
8888
"""Loads a segment data, from the projection data, into memory allowing for faster access."""
8989
if self.projdata is None:
9090
self.load_projdata()
9191

9292
if self.projdata is not None:
93-
self.segment_data = self.projdata.get_segment_by_view(segment_number)
93+
seg_idx = stir.SegmentIndices(segment_number, timing_pos)
94+
self.segment_data = self.projdata.get_segment_by_view(seg_idx)
9495
return self.segment_data
9596

9697
@staticmethod
@@ -120,21 +121,16 @@ def get_limits(self, dimension: ProjDataDims, segment_number: int) -> tuple:
120121
elif dimension == ProjDataDims.TANGENTIAL_POS:
121122
return self.projdata.get_min_tangential_pos_num(), \
122123
self.projdata.get_max_tangential_pos_num()
124+
elif dimension == ProjDataDims.TIMING_POS:
125+
return self.projdata.get_min_tof_pos_num(), \
126+
self.projdata.get_max_tof_pos_num()
123127
else:
124128
raise ValueError("Unknown sinogram dimension: " + str(dimension))
125129

126-
def get_num_indices(self, dimension: ProjDataDims):
130+
def get_num_indices(self, dimension: ProjDataDims) -> int:
127131
"""Returns the number of indices in the given dimension."""
128-
if dimension == ProjDataDims.SEGMENT_NUM:
129-
return self.projdata.get_num_segments()
130-
elif dimension == ProjDataDims.AXIAL_POS:
131-
return self.projdata.get_num_axial_poss(self.get_current_segment_num())
132-
elif dimension == ProjDataDims.VIEW_NUMBER:
133-
return self.projdata.get_num_views()
134-
elif dimension == ProjDataDims.TANGENTIAL_POS:
135-
return self.projdata.get_num_tangential_poss()
136-
else:
137-
raise ValueError("Unknown sinogram dimension: " + str(dimension))
132+
limits = self.get_limits(dimension, self.get_current_segment_num())
133+
return limits[1] - limits[0] + 1
138134

139135
def get_current_segment_num(self) -> int:
140136
"""Returns the segment number of the current segment data."""

examples/python/projdata_visualisation/BackendTools/UIGroupboxProjdataDimensions.py

Lines changed: 15 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
# Copyright 2022 University College London
1+
# Copyright 2022, 2024 University College London
22

33
# Author Robert Twyman
44

@@ -42,6 +42,10 @@ def __init__(self, stir_interface: ProjDataVisualisationBackend) -> QGroupBox:
4242
ProjDataDims.TANGENTIAL_POS: {
4343
'label': 'Tangential position',
4444
'connect_method': self.tangential_pos_refresh
45+
},
46+
ProjDataDims.TIMING_POS: {
47+
'label': 'TOF bin',
48+
'connect_method': self.timing_pos_refresh
4549
}
4650
}
4751

@@ -54,6 +58,7 @@ def __init__(self, stir_interface: ProjDataVisualisationBackend) -> QGroupBox:
5458
self.UI_slider_spinboxes[ProjDataDims.AXIAL_POS].add_item_to_layout(layout, row=2)
5559
self.UI_slider_spinboxes[ProjDataDims.VIEW_NUMBER].add_item_to_layout(layout, row=4)
5660
self.UI_slider_spinboxes[ProjDataDims.TANGENTIAL_POS].add_item_to_layout(layout, row=6)
61+
self.UI_slider_spinboxes[ProjDataDims.TIMING_POS].add_item_to_layout(layout, row=8)
5762

5863
layout.setRowStretch(5, 1)
5964
self.groupbox.setLayout(layout)
@@ -83,7 +88,8 @@ def segment_number_refresh(self):
8388
""" This function is called when the user changes the segment number value.
8489
Because of the way the STIR segment data is handled, the segment_data needs to change first."""
8590
new_segment_num = self.UI_slider_spinboxes[ProjDataDims.SEGMENT_NUM].value()
86-
self.stir_interface.refresh_segment_data(new_segment_num)
91+
new_timing_pos = self.UI_slider_spinboxes[ProjDataDims.TIMING_POS].value()
92+
self.stir_interface.refresh_segment_data(new_segment_num, new_timing_pos)
8793
self.UI_controller_UI_change_trigger()
8894

8995
def axial_pos_refresh(self):
@@ -98,6 +104,13 @@ def tangential_pos_refresh(self):
98104
"""This function is called when the user changes the tangential position value."""
99105
self.UI_controller_UI_change_trigger()
100106

107+
def timing_pos_refresh(self):
108+
"""This function is called when the user changes the TOF bin value."""
109+
new_segment_num = self.UI_slider_spinboxes[ProjDataDims.SEGMENT_NUM].value()
110+
new_timing_pos = self.UI_slider_spinboxes[ProjDataDims.TIMING_POS].value()
111+
self.stir_interface.refresh_segment_data(new_segment_num, new_timing_pos)
112+
self.UI_controller_UI_change_trigger()
113+
101114
def refresh_sliders_and_spinboxes_ranges(self) -> None:
102115
"""Update the sliders and spinboxes ranges based upon the stir_interface projdata."""
103116
if self.stir_interface.projdata is None:

examples/python/projdata_visualisation/ProjDataVisualisation.py

Lines changed: 19 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
#!/usr/bin/env python
22
# -*- coding: utf-8 -*-
33

4-
# Copyright 2022 University College London
4+
# Copyright 2022, 2024 University College London
55

66
# Author Robert Twyman
77

@@ -26,6 +26,7 @@
2626
from BackendTools.STIRInterface import ProjDataVisualisationBackend, ProjDataDims
2727
from BackendTools.UIGroupboxProjdataDimensions import UIGroupboxProjDataDimensions
2828

29+
import stir
2930

3031
class ProjDataVisualisationWidgetGallery(QDialog):
3132
def __init__(self, parent=None):
@@ -181,16 +182,20 @@ def update_display_image(self):
181182
image = self.get_sinogram_numpy_array()
182183
ax.title.set_text(
183184
f"Sinogram - Segment: {self.UI_groupbox_projdata_dimensions.value(ProjDataDims.SEGMENT_NUM)}, "
184-
f"Axial Position: {self.UI_groupbox_projdata_dimensions.value(ProjDataDims.AXIAL_POS)}")
185+
f"Axial Position: {self.UI_groupbox_projdata_dimensions.value(ProjDataDims.AXIAL_POS)}, "
186+
f"TOF bin: {self.UI_groupbox_projdata_dimensions.value(ProjDataDims.TIMING_POS)}")
185187
ax.yaxis.set_label_text("Views/projection angle")
186188
ax.xaxis.set_label_text("Tangential positions")
189+
ax.xaxis.set_label_text("TOF bins")
187190
elif self.viewgram_radio_button.isChecked():
188191
image = self.get_viewgram_numpy_array()
189192
ax.title.set_text(
190193
f"Sinogram - Segment: {self.UI_groupbox_projdata_dimensions.value(ProjDataDims.SEGMENT_NUM)},"
191-
f"View Number: {self.UI_groupbox_projdata_dimensions.value(ProjDataDims.VIEW_NUMBER)}")
194+
f"View Number: {self.UI_groupbox_projdata_dimensions.value(ProjDataDims.VIEW_NUMBER)}, "
195+
f"TOF bin: {self.UI_groupbox_projdata_dimensions.value(ProjDataDims.TIMING_POS)}")
192196
ax.yaxis.set_label_text("Axial positions")
193197
ax.xaxis.set_label_text("Tangential positions")
198+
ax.xaxis.set_label_text("TOF bins")
194199
else:
195200
msg = f"Error: No radio button is checked... How did you get here?\n"
196201
raise Exception(msg)
@@ -201,6 +206,14 @@ def update_display_image(self):
201206
)
202207
self.display_image_matplotlib_canvas.draw()
203208

209+
def get_bin(self) -> stir.Bin:
210+
view_num = self.UI_groupbox_projdata_dimensions.value(ProjDataDims.VIEW_NUMBER)
211+
axial_pos = self.UI_groupbox_projdata_dimensions.value(ProjDataDims.AXIAL_POS)
212+
segment_num = self.UI_groupbox_projdata_dimensions.value(ProjDataDims.SEGMENT_NUM)
213+
tangential_pos = self.UI_groupbox_projdata_dimensions.value(ProjDataDims.TANGENTIAL_POS)
214+
timing_pos = self.UI_groupbox_projdata_dimensions.value(ProjDataDims.TIMING_POS)
215+
return stir.Bin(segment_num, view_num, axial_pos, tangential_pos, timing_pos)
216+
204217
def get_sinogram_numpy_array(self):
205218
"""
206219
This function returns the sinogram numpy array based on the current UI configuration parameters for segment
@@ -209,16 +222,14 @@ def get_sinogram_numpy_array(self):
209222
if self.stir_interface.projdata is None:
210223
return None
211224

212-
axial_pos = self.UI_groupbox_projdata_dimensions.value(ProjDataDims.AXIAL_POS)
213225
return self.stir_interface.as_numpy(
214-
self.stir_interface.segment_data.get_sinogram(axial_pos))
226+
self.stir_interface.segment_data.get_sinogram(self.get_bin().axial_pos_num))
215227

216228
def get_viewgram_numpy_array(self):
217229
if self.stir_interface.projdata is None:
218230
return None
219231

220-
view_num = self.UI_groupbox_projdata_dimensions.value(ProjDataDims.VIEW_NUMBER)
221-
return self.stir_interface.as_numpy(self.stir_interface.segment_data.get_viewgram(view_num))
232+
return self.stir_interface.as_numpy(self.stir_interface.segment_data.get_viewgram(self.get_bin().view_num))
222233

223234
def browse_file_system_for_projdata(self):
224235
initial = self.projdata_filename_box.text()
@@ -256,7 +267,7 @@ def set_projdata(self, projdata):
256267

257268
def OpenProjDataVisualisation(projdata=None):
258269
"""
259-
Function to open the ProjDataVisualisation GUI window. Will not exit pyton on window close.
270+
Function to open the ProjDataVisualisation GUI window. Will not exit python on window close.
260271
projdata: Proj data to be visualised. Can be either a stir.ProjData object, a file path (str) or None. If None, an empty GUI will be opened.
261272
"""
262273
app = QApplication([])

src/include/stir/ProjData.inl

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -27,13 +27,13 @@ START_NAMESPACE_STIR
2727
SegmentBySinogram<float>
2828
ProjData::get_segment_by_sinogram(const SegmentIndices& si) const
2929
{
30-
return this->get_segment_by_sinogram(si.segment_num());
30+
return this->get_segment_by_sinogram(si.segment_num(), si.timing_pos_num());
3131
}
3232

3333
SegmentByView<float>
3434
ProjData::get_segment_by_view(const SegmentIndices& si) const
3535
{
36-
return this->get_segment_by_view(si.segment_num());
36+
return this->get_segment_by_view(si.segment_num(), si.timing_pos_num());
3737
}
3838

3939
Viewgram<float>

src/include/stir/Segment.h

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,8 @@
2323

2424
#include "stir/ProjDataInfo.h"
2525
#include "stir/SegmentIndices.h"
26+
#include "stir/SinogramIndices.h"
27+
#include "stir/ViewgramIndices.h"
2628
#include "stir/shared_ptr.h"
2729

2830
START_NAMESPACE_STIR
@@ -83,6 +85,11 @@ class Segment
8385
//! return a new viewgram, with data set as in the segment
8486
virtual Viewgram<elemT> get_viewgram(int view_num) const = 0;
8587

88+
//! return a new sinogram, with data set as in the segment
89+
inline Sinogram<elemT> get_sinogram(const SinogramIndices& s) const;
90+
//! return a new viewgram, with data set as in the segment
91+
inline Viewgram<elemT> get_viewgram(const ViewgramIndices&) const;
92+
8693
//! set data in segment according to sinogram \c s
8794
virtual void set_sinogram(const Sinogram<elemT>& s) = 0;
8895
//! set sinogram at a different axial_pos_num

src/include/stir/Segment.inl

Lines changed: 15 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@
33
/*
44
Copyright (C) 2000 PARAPET partners
55
Copyright (C) 2000- 2007, IRSL
6-
Copyright (C) 2023, University College London
6+
Copyright (C) 2023, 2024 University College London
77
This file is part of STIR.
88
99
SPDX-License-Identifier: Apache-2.0 AND License-ref-PARAPET-license
@@ -54,4 +54,18 @@ Segment<elemT>::get_proj_data_info_sptr() const
5454
return proj_data_info_sptr;
5555
}
5656

57+
template <typename elemT>
58+
Sinogram<elemT>
59+
Segment<elemT>::get_sinogram(const SinogramIndices& s) const
60+
{
61+
return this->get_sinogram(s.axial_pos_num());
62+
}
63+
64+
template <typename elemT>
65+
Viewgram<elemT>
66+
Segment<elemT>::get_viewgram(const ViewgramIndices& v) const
67+
{
68+
return this->get_viewgram(v.view_num());
69+
}
70+
5771
END_NAMESPACE_STIR

src/include/stir/SegmentBySinogram.h

Lines changed: 10 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -103,22 +103,24 @@ class SegmentBySinogram : public Segment<elemT>, public Array<3,elemT>
103103
inline int get_min_tangential_pos_num() const;
104104
//! Get maximum tangential position number
105105
inline int get_max_tangential_pos_num() const;
106+
using Segment<elemT>::get_sinogram;
107+
using Segment<elemT>::get_viewgram;
106108
//! Get sinogram
107-
inline Sinogram<elemT> get_sinogram(int axial_pos_num) const;
109+
inline Sinogram<elemT> get_sinogram(int axial_pos_num) const override;
108110
//! Get viewgram
109-
Viewgram<elemT> get_viewgram(int view_num) const;
111+
Viewgram<elemT> get_viewgram(int view_num) const override;
110112
//! Set viewgram
111-
void set_viewgram(const Viewgram<elemT>&);
113+
void set_viewgram(const Viewgram<elemT>&) override;
112114
//! Set sinogram
113-
inline void set_sinogram(Sinogram<elemT> const &s, int axial_pos_num);
114-
inline void set_sinogram(const Sinogram<elemT>& s);
115+
inline void set_sinogram(Sinogram<elemT> const &s, int axial_pos_num) override;
116+
inline void set_sinogram(const Sinogram<elemT>& s) override;
115117

116118
//! Overloading Array::grow
117-
void grow(const IndexRange<3>& range);
119+
void grow(const IndexRange<3>& range) override;
118120
//! Overloading Array::resize
119-
void resize(const IndexRange<3>& range);
121+
void resize(const IndexRange<3>& range) override;
120122

121-
virtual bool operator ==(const Segment<elemT>&) const;
123+
virtual bool operator ==(const Segment<elemT>&) const override;
122124
};
123125

124126
END_NAMESPACE_STIR

src/include/stir/SegmentByView.h

Lines changed: 10 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -106,23 +106,25 @@ template <typename elemT> class SegmentByView : public Segment<elemT>, public Ar
106106
//! Get maximum tangetial position number
107107
inline int get_max_tangential_pos_num() const;
108108

109+
using Segment<elemT>::get_sinogram;
110+
using Segment<elemT>::get_viewgram;
109111
//! Get sinogram
110-
Sinogram<elemT> get_sinogram(int axial_pos_num) const;
112+
Sinogram<elemT> get_sinogram(int axial_pos_num) const override;
111113
//! Get viewgram
112-
inline Viewgram<elemT> get_viewgram(int view_num) const;
114+
inline Viewgram<elemT> get_viewgram(int view_num) const override;
113115
//! Set sinogram
114-
inline void set_sinogram(const Sinogram<elemT> &s);
116+
inline void set_sinogram(const Sinogram<elemT> &s) override;
115117
//! Set sinogram
116-
void set_sinogram(Sinogram<elemT> const &s, int axial_pos_num);
118+
void set_sinogram(Sinogram<elemT> const &s, int axial_pos_num) override;
117119
//! Set viewgram
118-
inline void set_viewgram(const Viewgram<elemT> &v);
120+
inline void set_viewgram(const Viewgram<elemT> &v) override;
119121

120122
//! Overloading Array::grow
121-
void grow(const IndexRange<3>& range);
123+
void grow(const IndexRange<3>& range) override;
122124
//! Overloading Array::resize
123-
void resize(const IndexRange<3>& range);
125+
void resize(const IndexRange<3>& range) override;
124126

125-
virtual bool operator ==(const Segment<elemT>&) const;
127+
virtual bool operator ==(const Segment<elemT>&) const override;
126128
};
127129

128130
END_NAMESPACE_STIR

0 commit comments

Comments
 (0)