Thanks to visit codestin.com
Credit goes to github.com

Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions documentation/release_4.0.htm
Original file line number Diff line number Diff line change
Expand Up @@ -159,6 +159,8 @@ <h3>New functionality</h3>
<li><tt>list_image_values</tt> now has options to print coordinates in LPS as opposed to indices, print in CSV, or suppress the title row.</li>
<li><tt>list_lm_countrates</tt> is a new list mode utility to output total counts per specified time interval.</li>
<li>List-mode classes for Siemens ECAT8 (32-bit).</li>
<li>Normalisation class for Siemens ECAT8, allowing for the <i>virtual</i> crystals. This needed 2 extra functions in <code>Scanner</code>
to know how many virtual crystals there are.</li>
<li>add Verbosity level (currently only to be set from interactive languages)</li>
<li>Added the anatomical parallel level sets (PLS) prior. If an uniform anatomical image is provided it will work as a (smoothed) total variation (TV) prior. </li>
<li>We added <code>SeparableGaussianArrayFilter</code> and <code>SeparableGaussianImageFilter</code> (based on old code in <tt>experimental</tt> which has now been removed). This filter constructs the kernel by sampling a Gaussian. It is therefore different from the Metz filter with power 0, as that is constructed by sampling a Gaussian in frequency space (and can therefore have negative values in image space).</li>
Expand Down
2 changes: 1 addition & 1 deletion src/IO/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,7 @@ set(${dir_LIB_SOURCES}
MultiDynamicDiscretisedDensityOutputFileFormat

GIPL_ImageFormat
stir_ecat_common
)

# we can use IO from the itk library
Expand All @@ -49,7 +50,6 @@ if (LLN_FOUND)
list(APPEND ${dir_LIB_SOURCES}
ECAT7OutputFileFormat stir_ecat7
ECAT6OutputFileFormat
stir_ecat_common
ECAT7ParametricDensityOutputFileFormat
ECAT7DynamicDiscretisedDensityOutputFileFormat
ECAT7DynamicDiscretisedDensityInputFileFormat
Expand Down
14 changes: 4 additions & 10 deletions src/IO/InterfileHeaderSiemens.cxx
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,8 @@
Copyright (C) 2000 PARAPET partners
Copyright (C) 2000 - 2009-04-30, Hammersmith Imanet Ltd
Copyright (C) 2011-07-01 - 2012, Kris Thielemans
Copyright (C) 2013, 2016 University College London
Copyright (C) 2013, 2016, 2018, 2020 University College London
Copyright (C) 2018 STFC
This file is part of STIR.

This file is free software; you can redistribute it and/or modify
Expand Down Expand Up @@ -33,6 +34,7 @@
#include "stir/ImagingModality.h"
#include "stir/ProjDataInfoCylindricalArcCorr.h"
#include "stir/ProjDataInfoCylindricalNoArcCorr.h"
#include "stir/IO/stir_ecat_common.h"
#include <numeric>
#include <functional>

Expand Down Expand Up @@ -269,15 +271,7 @@ bool InterfileRawDataHeaderSiemens::post_processing()
{
error("Interfile warning: 'number of segments' and length of 'segment table' are not consistent");
}
// same order as in stir_ecat7
// Siemens always stores segments as 0, -1, +1, ...
segment_sequence.resize(num_segments);
segment_sequence[0] = 0;
for (int segment_num = 1; segment_num <= num_segments/2; ++segment_num)
{
segment_sequence[2 * segment_num - 1] = -segment_num;
segment_sequence[2 * segment_num] = segment_num;
}
segment_sequence = ecat::find_segment_sequence(*data_info_ptr);
//XXX check if order here and segment_table are consistent
}

Expand Down
11 changes: 2 additions & 9 deletions src/IO/stir_ecat7.cxx
Original file line number Diff line number Diff line change
Expand Up @@ -1379,15 +1379,8 @@ make_pdfs_from_matrix_aux(SUBHEADERPTR sub_header_ptr,

pdi_ptr->set_num_axial_poss_per_segment(num_axial_poss_per_seg);

std::vector<int> segment_sequence_in_stream(2*max_segment_num+1);
// KT 25/10/2000 swapped segment order
// ECAT 7 always stores segments as 0, -1, +1, ...
segment_sequence_in_stream[0] = 0;
for (int segment_num = 1; segment_num<=max_segment_num; ++segment_num)
{
segment_sequence_in_stream[2*segment_num-1] = -segment_num;
segment_sequence_in_stream[2*segment_num] = segment_num;
}
std::vector<int> segment_sequence_in_stream =
find_segment_sequence_in_stream(*pdi_ptr);

Matval matval;
mat_numdoc(matrix->matnum, &matval);
Expand Down
20 changes: 19 additions & 1 deletion src/IO/stir_ecat_common.cxx
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@
\file
\ingroup ECAT

\brief Implementation of routines which convert ECAT6 and ECAT7 things into our building blocks and vice versa.
\brief Implementation of routines which convert ECAT6, ECAT7 and ECAT8 things into our building blocks and vice versa.

\author Kris Thielemans
\author PARAPET project
Expand All @@ -13,6 +13,7 @@
/*
Copyright (C) 2000 PARAPET partners
Copyright (C) 2000- 2009, Hammersmith Imanet Ltd
Copyright (C) 2020, University College London
This file is part of STIR.

This file is free software; you can redistribute it and/or modify
Expand All @@ -31,6 +32,7 @@
#include "stir/ByteOrder.h"
#include "stir/NumericType.h"
#include "stir/Scanner.h"
#include "stir/ProjDataInfo.h"
#include "stir/IO/stir_ecat_common.h"

START_NAMESPACE_STIR
Expand Down Expand Up @@ -187,5 +189,21 @@ Scanner* find_scanner_from_ECAT_system_type(const short system_type)
}
}

std::vector<int>
find_segment_sequence(const ProjDataInfo& pdi)
{
const int max_segment_num = pdi.get_max_segment_num();
std::vector<int> segment_sequence(2*max_segment_num+1);
// KT 25/10/2000 swapped segment order
// ECAT 7 always stores segments as 0, -1, +1, ...
segment_sequence[0] = 0;
for (int segment_num = 1; segment_num<=max_segment_num; ++segment_num)
{
segment_sequence[2*segment_num-1] = -segment_num;
segment_sequence[2*segment_num] = segment_num;
}
return segment_sequence;
}

END_NAMESPACE_ECAT
END_NAMESPACE_STIR
54 changes: 48 additions & 6 deletions src/buildblock/Scanner.cxx
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@
Copyright (C) 2000 - 2010-07-21, Hammersmith Imanet Ltd
Copyright (C) 2011, Kris Thielemans
Copyright (C) 2010-2013, King's College London
Copyright (C) 2013-2016, University College London
Copyright (C) 2013-2016,2019, University College London
This file is part of STIR.

This file is free software; you can redistribute it and/or modify
Expand Down Expand Up @@ -189,9 +189,9 @@ Scanner::Scanner(Type scanner_type)
set_params(E1080, string_list("ECAT 1080", "Biograph 16", "1080"),
41, 336, 2* 336,
412.0F, 7.0F, 4.0F, 2.000F, 0.0F,
1, 2, 41, 14, 41, 14, 1);
1, 2, 13+1, 13+1, 0, 0, 1);// TODO bucket/singles info?
// Transaxial blocks have 13 physical crystals and a gap at the
// 140th crystal where the counts are zero.
// 14th crystal where the counts are zero.
// There are 39 rings with 13 axial crystals per block. Two virtual
// rings are added, but contain counts after applying axial compression.
break;
Expand All @@ -207,6 +207,16 @@ Scanner::Scanner(Type scanner_type)
0.145f, 511.f); // TODO bucket/singles info incorrect? 224 buckets in total, but not sure how distributed
break;

case Siemens_mCT:
// 13x13 blocks, 1 virtual "crystal" along axial and transaxial direction, 48 blocks along the ring, 4 blocks in axial direction
set_params(Siemens_mCT, string_list("Siemens mCT", "mCT", "2011", "1104" /* used in norm files */),
55, 400, (13+1)*48,
421.0F, 7.0F, 4.054F, 2.005F, 0.0F,
4, 1, 13+1, 13+1, 0,0, 1 ); // TODO singles info incorrect
// energy: 435-650
// 13 TOF bins
break;

case RPT:

set_params(RPT, string_list("PRT-1", "RPT"),
Expand Down Expand Up @@ -598,7 +608,38 @@ set_params(Type type_v,const list<string>& list_of_names_v,

}

/*! \todo The current list is bound to be incomplete. would be better to stick it in set_params().
*/
int
Scanner::
get_num_virtual_axial_crystals_per_block() const
{
switch(get_type())
{
case E1080:
case Siemens_mCT:
return 1;
default:
return 0;
}
}

/*! \todo The current list is bound to be incomplete. would be better to stick it in set_params().
*/
int
Scanner::
get_num_virtual_transaxial_crystals_per_block() const
{
switch(get_type())
{
case E1080:
case Siemens_mCT:
case Siemens_mMR:
return 1;
default:
return 0;
}
}

Succeeded
Scanner::
Expand Down Expand Up @@ -654,10 +695,11 @@ check_consistency() const
const int dets_axial =
get_num_axial_blocks() *
get_num_axial_crystals_per_block();
if ( dets_axial != get_num_rings())
if ( dets_axial != (get_num_rings() + get_num_virtual_axial_crystals_per_block()))
{
warning("Scanner %s: inconsistent axial block info",
this->get_name().c_str());
warning("Scanner %s: inconsistent axial block info: %d vs %d",
this->get_name().c_str(),
dets_axial, get_num_rings() + get_num_virtual_axial_crystals_per_block());
return Succeeded::no;
}
}
Expand Down
10 changes: 10 additions & 0 deletions src/include/stir/IO/stir_ecat_common.h
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,7 @@
#define __stir_IO_stir_ecat_common_H__

#include "stir/common.h"
#include <vector>

//*************** namespace macros
#if !defined(STIR_NO_NAMESPACE)
Expand Down Expand Up @@ -68,6 +69,7 @@ START_NAMESPACE_STIR
class NumericType;
class ByteOrder;
class Scanner;
class ProjDataInfo;

START_NAMESPACE_ECAT

Expand Down Expand Up @@ -115,6 +117,14 @@ short find_ECAT_system_type(const Scanner& scanner);
Returns a Scanner(Scanner::Unknown_Scanner) object when the scanner is not recognised. */
Scanner* find_scanner_from_ECAT_system_type(const short system_type);

//! Return the sequence of how Siemens stores segments
/*!
\ingroup ECAT
ECAT 7,8 always stores segments as 0, -1, +1, ... (numbering is in STIR convention)
*/
std::vector<int>
find_segment_sequence(const ProjDataInfo& pdi);

END_NAMESPACE_ECAT

END_NAMESPACE_STIR
Expand Down
18 changes: 16 additions & 2 deletions src/include/stir/Scanner.h
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@
Copyright (C) 2000 PARAPET partners
Copyright (C) 2000-2010, Hammersmith Imanet Ltd
Copyright (C) 2011-2013, King's College London
Copyright (C) 2016, UCL
Copyright (C) 2016, 2019, UCL

This file is part of STIR.

Expand Down Expand Up @@ -77,6 +77,11 @@ class Succeeded;
collection of blocks. A \c singles_unit is then a set of crystals
for which we can get singles rates.

A further complication is that some scanners (including many Siemens scanners)
insert virtual crystals in the sinogram data (corresponding to gaps between
detector blocks). We currently define the blocks as the "virtual" ones,
but provide extra members to find out how many of these virtual crystals there are.

\warning This information is only sensible for discrete detector-based scanners.
\todo Some scanners do not have all info filled in at present. Values are then
set to 0.
Expand Down Expand Up @@ -108,7 +113,7 @@ class Scanner
to flag up an error and do some guess work in trying to recognise the scanner from
any given parameters.
*/
enum Type {E931, E951, E953, E921, E925, E961, E962, E966, E1080, Siemens_mMR, RPT,HiDAC,
enum Type {E931, E951, E953, E921, E925, E961, E962, E966, E1080, Siemens_mMR, Siemens_mCT, RPT,HiDAC,
Advance, DiscoveryLS, DiscoveryST, DiscoverySTE, DiscoveryRX, Discovery600, Discovery690,
HZLR, RATPET, PANDA, HYPERimage, nanoPET, HRRT, Allegro, GeminiTF, User_defined_scanner,
Unknown_scanner};
Expand Down Expand Up @@ -262,6 +267,15 @@ class Scanner
/* inline int get_num_layers_singles_units() const; */
inline int get_num_singles_units() const;

//! \name number of "fake" crystals per block, inserted by the scanner
/*! Some scanners (including many Siemens scanners) insert virtual crystals in the sinogram data.
The other members of the class return the size of the "virtual" block. With these
functions you can find its true size.
*/
//@{!
int get_num_virtual_axial_crystals_per_block() const;
int get_num_virtual_transaxial_crystals_per_block() const;
//@}

//@} (end of block/bucket info)

Expand Down
6 changes: 4 additions & 2 deletions src/include/stir/Scanner.inl
Original file line number Diff line number Diff line change
Expand Up @@ -152,7 +152,9 @@ Scanner::get_num_detector_layers() const
int
Scanner::get_num_axial_blocks() const
{
return num_rings/num_axial_crystals_per_block;
// when using virtual crystals between blocks, there won't be one at the end, so we
// need to take this into account.
return (num_rings+get_num_virtual_axial_crystals_per_block())/num_axial_crystals_per_block;
}

int
Expand Down Expand Up @@ -194,7 +196,7 @@ Scanner::get_num_axial_singles_units() const
if ( num_axial_crystals_per_singles_unit == 0 ) {
return 0;
} else {
return num_rings / num_axial_crystals_per_singles_unit;
return (num_rings+get_num_virtual_axial_crystals_per_block()) / num_axial_crystals_per_singles_unit;
}
}

Expand Down
24 changes: 8 additions & 16 deletions src/listmode_buildblock/CListRecordECAT8_32bit.cxx
Original file line number Diff line number Diff line change
Expand Up @@ -28,7 +28,7 @@
#include "stir/Bin.h"
#include "stir/error.h"
#include "stir/Succeeded.h"

#include "stir/IO/stir_ecat_common.h"
#include <algorithm>
#include <iostream>
START_NAMESPACE_STIR
Expand All @@ -42,22 +42,13 @@ CListEventECAT8_32bit(const shared_ptr<ProjDataInfo>& proj_data_info_sptr) :
dynamic_cast<const ProjDataInfoCylindricalNoArcCorr * const>(proj_data_info_sptr.get());
if (proj_data_info_ptr == 0)
error("CListEventECAT8_32bit can only be initialised with cylindrical projection data without arc-correction");
if (proj_data_info_ptr->get_max_ring_difference(0) != proj_data_info_ptr->get_min_ring_difference(0))
error("CListEventECAT8_32bit can only handle axial compression==1");

const int num_rings = this->scanner_sptr->get_num_rings();
const int max_ring_diff=proj_data_info_ptr->get_max_ring_difference(proj_data_info_ptr->get_max_segment_num());
if (proj_data_info_ptr->get_max_ring_difference(0) != proj_data_info_ptr->get_min_ring_difference(0))
error("CListEventECAT8_32bit can only handle axial compression==1");
this->segment_sequence.resize(2*max_ring_diff+1);
this->sizes.resize(2*max_ring_diff+1);
this->segment_sequence[0]=0;
this->sizes[0]=num_rings;
for (int ringdiff=1; ringdiff<=max_ring_diff; ++ringdiff)
{
this->segment_sequence[2*ringdiff-1]=-ringdiff;
this->segment_sequence[2*ringdiff]=ringdiff;
this->sizes[2*ringdiff-1]=num_rings-ringdiff;
this->sizes[2*ringdiff]=num_rings-ringdiff;
}
this->segment_sequence = ecat::find_segment_sequence(*proj_data_info_ptr);
this->sizes.resize(this->segment_sequence.size());
for (unsigned s=0U; s <= this->segment_sequence.size(); ++s)
this->sizes[s]=proj_data_info_ptr->get_num_axial_poss(segment_sequence[s]);
}

void
Expand Down Expand Up @@ -87,6 +78,7 @@ get_detection_position(DetectionPositionPair<>& det_pos) const
z -= this->sizes[i];
}
}
// this is actually a compressed bin for many Siemens scanners. would have to go to det_pos somehow, or overload get_bin
const Bin uncompressed_bin(segment_num, view_num, axial_pos_num,tang_pos_num - (num_tangential_poss/2));
this->get_uncompressed_proj_data_info_sptr()->get_det_pos_pair_for_bin(det_pos,uncompressed_bin);
}
Expand Down
Loading