Skip to content

Commit

Permalink
Merge pull request #1319 from KrisThielemans/TOF_segment_and_vis
Browse files Browse the repository at this point in the history
TOF segment bug fix, extra members and update visualation to add TOF
  • Loading branch information
KrisThielemans authored Jan 11, 2024
2 parents 75a9342 + c0c4c5b commit 151c457
Show file tree
Hide file tree
Showing 8 changed files with 89 additions and 44 deletions.
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
# Copyright 2022 University College London
# Copyright 2022, 2024 University College London

# Author Robert Twyman

Expand All @@ -23,7 +23,7 @@ class ProjDataDims(Enum):
AXIAL_POS = auto()
VIEW_NUMBER = auto()
TANGENTIAL_POS = auto()

TIMING_POS = auto()

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

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

if self.projdata is not None:
self.segment_data = self.projdata.get_segment_by_view(segment_number)
seg_idx = stir.SegmentIndices(segment_number, timing_pos)
self.segment_data = self.projdata.get_segment_by_view(seg_idx)
return self.segment_data

@staticmethod
Expand Down Expand Up @@ -120,21 +121,16 @@ def get_limits(self, dimension: ProjDataDims, segment_number: int) -> tuple:
elif dimension == ProjDataDims.TANGENTIAL_POS:
return self.projdata.get_min_tangential_pos_num(), \
self.projdata.get_max_tangential_pos_num()
elif dimension == ProjDataDims.TIMING_POS:
return self.projdata.get_min_tof_pos_num(), \
self.projdata.get_max_tof_pos_num()
else:
raise ValueError("Unknown sinogram dimension: " + str(dimension))

def get_num_indices(self, dimension: ProjDataDims):
def get_num_indices(self, dimension: ProjDataDims) -> int:
"""Returns the number of indices in the given dimension."""
if dimension == ProjDataDims.SEGMENT_NUM:
return self.projdata.get_num_segments()
elif dimension == ProjDataDims.AXIAL_POS:
return self.projdata.get_num_axial_poss(self.get_current_segment_num())
elif dimension == ProjDataDims.VIEW_NUMBER:
return self.projdata.get_num_views()
elif dimension == ProjDataDims.TANGENTIAL_POS:
return self.projdata.get_num_tangential_poss()
else:
raise ValueError("Unknown sinogram dimension: " + str(dimension))
limits = self.get_limits(dimension, self.get_current_segment_num())
return limits[1] - limits[0] + 1

def get_current_segment_num(self) -> int:
"""Returns the segment number of the current segment data."""
Expand Down
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
# Copyright 2022 University College London
# Copyright 2022, 2024 University College London

# Author Robert Twyman

Expand Down Expand Up @@ -42,6 +42,10 @@ def __init__(self, stir_interface: ProjDataVisualisationBackend) -> QGroupBox:
ProjDataDims.TANGENTIAL_POS: {
'label': 'Tangential position',
'connect_method': self.tangential_pos_refresh
},
ProjDataDims.TIMING_POS: {
'label': 'TOF bin',
'connect_method': self.timing_pos_refresh
}
}

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

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

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

def timing_pos_refresh(self):
"""This function is called when the user changes the TOF bin value."""
new_segment_num = self.UI_slider_spinboxes[ProjDataDims.SEGMENT_NUM].value()
new_timing_pos = self.UI_slider_spinboxes[ProjDataDims.TIMING_POS].value()
self.stir_interface.refresh_segment_data(new_segment_num, new_timing_pos)
self.UI_controller_UI_change_trigger()

def refresh_sliders_and_spinboxes_ranges(self) -> None:
"""Update the sliders and spinboxes ranges based upon the stir_interface projdata."""
if self.stir_interface.projdata is None:
Expand Down
27 changes: 19 additions & 8 deletions examples/python/projdata_visualisation/ProjDataVisualisation.py
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
#!/usr/bin/env python
# -*- coding: utf-8 -*-

# Copyright 2022 University College London
# Copyright 2022, 2024 University College London

# Author Robert Twyman

Expand All @@ -26,6 +26,7 @@
from BackendTools.STIRInterface import ProjDataVisualisationBackend, ProjDataDims
from BackendTools.UIGroupboxProjdataDimensions import UIGroupboxProjDataDimensions

import stir

class ProjDataVisualisationWidgetGallery(QDialog):
def __init__(self, parent=None):
Expand Down Expand Up @@ -181,16 +182,20 @@ def update_display_image(self):
image = self.get_sinogram_numpy_array()
ax.title.set_text(
f"Sinogram - Segment: {self.UI_groupbox_projdata_dimensions.value(ProjDataDims.SEGMENT_NUM)}, "
f"Axial Position: {self.UI_groupbox_projdata_dimensions.value(ProjDataDims.AXIAL_POS)}")
f"Axial Position: {self.UI_groupbox_projdata_dimensions.value(ProjDataDims.AXIAL_POS)}, "
f"TOF bin: {self.UI_groupbox_projdata_dimensions.value(ProjDataDims.TIMING_POS)}")
ax.yaxis.set_label_text("Views/projection angle")
ax.xaxis.set_label_text("Tangential positions")
ax.xaxis.set_label_text("TOF bins")
elif self.viewgram_radio_button.isChecked():
image = self.get_viewgram_numpy_array()
ax.title.set_text(
f"Sinogram - Segment: {self.UI_groupbox_projdata_dimensions.value(ProjDataDims.SEGMENT_NUM)},"
f"View Number: {self.UI_groupbox_projdata_dimensions.value(ProjDataDims.VIEW_NUMBER)}")
f"View Number: {self.UI_groupbox_projdata_dimensions.value(ProjDataDims.VIEW_NUMBER)}, "
f"TOF bin: {self.UI_groupbox_projdata_dimensions.value(ProjDataDims.TIMING_POS)}")
ax.yaxis.set_label_text("Axial positions")
ax.xaxis.set_label_text("Tangential positions")
ax.xaxis.set_label_text("TOF bins")
else:
msg = f"Error: No radio button is checked... How did you get here?\n"
raise Exception(msg)
Expand All @@ -201,6 +206,14 @@ def update_display_image(self):
)
self.display_image_matplotlib_canvas.draw()

def get_bin(self) -> stir.Bin:
view_num = self.UI_groupbox_projdata_dimensions.value(ProjDataDims.VIEW_NUMBER)
axial_pos = self.UI_groupbox_projdata_dimensions.value(ProjDataDims.AXIAL_POS)
segment_num = self.UI_groupbox_projdata_dimensions.value(ProjDataDims.SEGMENT_NUM)
tangential_pos = self.UI_groupbox_projdata_dimensions.value(ProjDataDims.TANGENTIAL_POS)
timing_pos = self.UI_groupbox_projdata_dimensions.value(ProjDataDims.TIMING_POS)
return stir.Bin(segment_num, view_num, axial_pos, tangential_pos, timing_pos)

def get_sinogram_numpy_array(self):
"""
This function returns the sinogram numpy array based on the current UI configuration parameters for segment
Expand All @@ -209,16 +222,14 @@ def get_sinogram_numpy_array(self):
if self.stir_interface.projdata is None:
return None

axial_pos = self.UI_groupbox_projdata_dimensions.value(ProjDataDims.AXIAL_POS)
return self.stir_interface.as_numpy(
self.stir_interface.segment_data.get_sinogram(axial_pos))
self.stir_interface.segment_data.get_sinogram(self.get_bin().axial_pos_num))

def get_viewgram_numpy_array(self):
if self.stir_interface.projdata is None:
return None

view_num = self.UI_groupbox_projdata_dimensions.value(ProjDataDims.VIEW_NUMBER)
return self.stir_interface.as_numpy(self.stir_interface.segment_data.get_viewgram(view_num))
return self.stir_interface.as_numpy(self.stir_interface.segment_data.get_viewgram(self.get_bin().view_num))

def browse_file_system_for_projdata(self):
initial = self.projdata_filename_box.text()
Expand Down Expand Up @@ -256,7 +267,7 @@ def set_projdata(self, projdata):

def OpenProjDataVisualisation(projdata=None):
"""
Function to open the ProjDataVisualisation GUI window. Will not exit pyton on window close.
Function to open the ProjDataVisualisation GUI window. Will not exit python on window close.
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.
"""
app = QApplication([])
Expand Down
4 changes: 2 additions & 2 deletions src/include/stir/ProjData.inl
Original file line number Diff line number Diff line change
Expand Up @@ -27,13 +27,13 @@ START_NAMESPACE_STIR
SegmentBySinogram<float>
ProjData::get_segment_by_sinogram(const SegmentIndices& si) const
{
return this->get_segment_by_sinogram(si.segment_num());
return this->get_segment_by_sinogram(si.segment_num(), si.timing_pos_num());
}

SegmentByView<float>
ProjData::get_segment_by_view(const SegmentIndices& si) const
{
return this->get_segment_by_view(si.segment_num());
return this->get_segment_by_view(si.segment_num(), si.timing_pos_num());
}

Viewgram<float>
Expand Down
7 changes: 7 additions & 0 deletions src/include/stir/Segment.h
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,8 @@

#include "stir/ProjDataInfo.h"
#include "stir/SegmentIndices.h"
#include "stir/SinogramIndices.h"
#include "stir/ViewgramIndices.h"
#include "stir/shared_ptr.h"

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

//! return a new sinogram, with data set as in the segment
inline Sinogram<elemT> get_sinogram(const SinogramIndices& s) const;
//! return a new viewgram, with data set as in the segment
inline Viewgram<elemT> get_viewgram(const ViewgramIndices&) const;

//! set data in segment according to sinogram \c s
virtual void set_sinogram(const Sinogram<elemT>& s) = 0;
//! set sinogram at a different axial_pos_num
Expand Down
16 changes: 15 additions & 1 deletion src/include/stir/Segment.inl
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@
/*
Copyright (C) 2000 PARAPET partners
Copyright (C) 2000- 2007, IRSL
Copyright (C) 2023, University College London
Copyright (C) 2023, 2024 University College London
This file is part of STIR.
SPDX-License-Identifier: Apache-2.0 AND License-ref-PARAPET-license
Expand Down Expand Up @@ -54,4 +54,18 @@ Segment<elemT>::get_proj_data_info_sptr() const
return proj_data_info_sptr;
}

template <typename elemT>
Sinogram<elemT>
Segment<elemT>::get_sinogram(const SinogramIndices& s) const
{
return this->get_sinogram(s.axial_pos_num());
}

template <typename elemT>
Viewgram<elemT>
Segment<elemT>::get_viewgram(const ViewgramIndices& v) const
{
return this->get_viewgram(v.view_num());
}

END_NAMESPACE_STIR
18 changes: 10 additions & 8 deletions src/include/stir/SegmentBySinogram.h
Original file line number Diff line number Diff line change
Expand Up @@ -103,22 +103,24 @@ class SegmentBySinogram : public Segment<elemT>, public Array<3,elemT>
inline int get_min_tangential_pos_num() const;
//! Get maximum tangential position number
inline int get_max_tangential_pos_num() const;
using Segment<elemT>::get_sinogram;
using Segment<elemT>::get_viewgram;
//! Get sinogram
inline Sinogram<elemT> get_sinogram(int axial_pos_num) const;
inline Sinogram<elemT> get_sinogram(int axial_pos_num) const override;
//! Get viewgram
Viewgram<elemT> get_viewgram(int view_num) const;
Viewgram<elemT> get_viewgram(int view_num) const override;
//! Set viewgram
void set_viewgram(const Viewgram<elemT>&);
void set_viewgram(const Viewgram<elemT>&) override;
//! Set sinogram
inline void set_sinogram(Sinogram<elemT> const &s, int axial_pos_num);
inline void set_sinogram(const Sinogram<elemT>& s);
inline void set_sinogram(Sinogram<elemT> const &s, int axial_pos_num) override;
inline void set_sinogram(const Sinogram<elemT>& s) override;

//! Overloading Array::grow
void grow(const IndexRange<3>& range);
void grow(const IndexRange<3>& range) override;
//! Overloading Array::resize
void resize(const IndexRange<3>& range);
void resize(const IndexRange<3>& range) override;

virtual bool operator ==(const Segment<elemT>&) const;
virtual bool operator ==(const Segment<elemT>&) const override;
};

END_NAMESPACE_STIR
Expand Down
18 changes: 10 additions & 8 deletions src/include/stir/SegmentByView.h
Original file line number Diff line number Diff line change
Expand Up @@ -106,23 +106,25 @@ template <typename elemT> class SegmentByView : public Segment<elemT>, public Ar
//! Get maximum tangetial position number
inline int get_max_tangential_pos_num() const;

using Segment<elemT>::get_sinogram;
using Segment<elemT>::get_viewgram;
//! Get sinogram
Sinogram<elemT> get_sinogram(int axial_pos_num) const;
Sinogram<elemT> get_sinogram(int axial_pos_num) const override;
//! Get viewgram
inline Viewgram<elemT> get_viewgram(int view_num) const;
inline Viewgram<elemT> get_viewgram(int view_num) const override;
//! Set sinogram
inline void set_sinogram(const Sinogram<elemT> &s);
inline void set_sinogram(const Sinogram<elemT> &s) override;
//! Set sinogram
void set_sinogram(Sinogram<elemT> const &s, int axial_pos_num);
void set_sinogram(Sinogram<elemT> const &s, int axial_pos_num) override;
//! Set viewgram
inline void set_viewgram(const Viewgram<elemT> &v);
inline void set_viewgram(const Viewgram<elemT> &v) override;

//! Overloading Array::grow
void grow(const IndexRange<3>& range);
void grow(const IndexRange<3>& range) override;
//! Overloading Array::resize
void resize(const IndexRange<3>& range);
void resize(const IndexRange<3>& range) override;

virtual bool operator ==(const Segment<elemT>&) const;
virtual bool operator ==(const Segment<elemT>&) const override;
};

END_NAMESPACE_STIR
Expand Down

0 comments on commit 151c457

Please sign in to comment.