/*
This file is part of CASToR.
CASToR is free software: you can redistribute it and/or modify it under the
terms of the GNU General Public License as published by the Free Software
Foundation, either version 3 of the License, or (at your option) any later
version.
CASToR is distributed in the hope that it will be useful, but WITHOUT ANY
WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
FOR A PARTICULAR PURPOSE. See the GNU General Public License for more
details.
You should have received a copy of the GNU General Public License along with
CASToR (in file GNU_GPL.TXT). If not, see .
Copyright 2017-2019 all CASToR contributors listed below:
--> Didier BENOIT, Claude COMTAT, Marina FILIPOVIC, Thibaut MERLIN, Mael MILLARDET, Simon STUTE, Valentin VIELZEUF
This is CASToR version 3.0.1.
*/
/*!
\file
\ingroup projector
\brief Implementation of class iProjectorJoseph
*/
#include "iProjectorJoseph.hh"
#include "sOutputManager.hh"
#include
#ifdef _WIN32
// Avoid compilation errors due to mix up between std::min()/max() and
// min max macros
#undef min
#undef max
#endif
// =====================================================================
// ---------------------------------------------------------------------
// ---------------------------------------------------------------------
// =====================================================================
iProjectorJoseph::iProjectorJoseph() : vProjector()
{
// This projector is not compatible with SPECT attenuation correction because
// the voxels contributing to the line are not strictly ordered with respect to
// their distance to point2 (due to interpolations at each plane crossed)
m_compatibleWithSPECTAttenuationCorrection = false;
// This projector is compatible with compression as it works only with the
// cartesian coordinates of 2 end points of a line
m_compatibleWithCompression = true;
// Default pointers and parameters
mp_boundariesX = nullptr;
mp_boundariesY = nullptr;
mp_boundariesZ = nullptr;
mp_maskPad = nullptr;
m_toleranceX = 0.;
m_toleranceY = 0.;
m_toleranceZ = 0.;
}
// =====================================================================
// ---------------------------------------------------------------------
// ---------------------------------------------------------------------
// =====================================================================
iProjectorJoseph::~iProjectorJoseph()
{
if ( mp_boundariesX )
{
delete[] mp_boundariesX;
mp_boundariesX = nullptr;
}
if ( mp_boundariesY )
{
delete[] mp_boundariesY;
mp_boundariesY = nullptr;
}
if ( mp_boundariesZ )
{
delete[] mp_boundariesZ;
mp_boundariesZ = nullptr;
}
if ( mp_maskPad )
{
delete[] mp_maskPad;
mp_maskPad = nullptr;
}
}
// =====================================================================
// ---------------------------------------------------------------------
// ---------------------------------------------------------------------
// =====================================================================
int iProjectorJoseph::ReadConfigurationFile(const string& a_configurationFile)
{
// No options for joseph
;
// Normal end
return 0;
}
// =====================================================================
// ---------------------------------------------------------------------
// ---------------------------------------------------------------------
// =====================================================================
int iProjectorJoseph::ReadOptionsList(const string& a_optionsList)
{
// No options for joseph
;
// Normal end
return 0;
}
// =====================================================================
// ---------------------------------------------------------------------
// ---------------------------------------------------------------------
// =====================================================================
void iProjectorJoseph::ShowHelpSpecific()
{
cout << "This projector is a line projector that uses linear interpolation between pixels." << endl;
cout << "It is implemented from the following published paper:" << endl;
cout << "P. M. Joseph, \"An improved algorithm for reprojecting rays through pixel images\", IEEE Trans. Med. Imaging, vol. 1, pp. 192-6, 1982." << endl;
}
// =====================================================================
// ---------------------------------------------------------------------
// ---------------------------------------------------------------------
// =====================================================================
int iProjectorJoseph::CheckSpecificParameters()
{
// Nothing to check for this projector
;
// Normal end
return 0;
}
// =====================================================================
// ---------------------------------------------------------------------
// ---------------------------------------------------------------------
// =====================================================================
int iProjectorJoseph::InitializeSpecific()
{
// Verbose
if (m_verbose>=2) Cout("iProjectorJoseph::InitializeSpecific() -> Use Joseph projector" << endl);
// Allocate and compute boundaries (grid through voxel centers)
mp_boundariesX = new HPFLTNB[ mp_nbVox[ 0 ] + 2 ];
for( INTNB i = 0; i < mp_nbVox[ 0 ] + 2; ++i ) mp_boundariesX[ i ] = -((HPFLTNB)(mp_halfFOV[ 0 ])) + ((HPFLTNB)(mp_sizeVox[ 0 ])) * ( ((HPFLTNB)i) - 0.5 );
mp_boundariesY = new HPFLTNB[ mp_nbVox[ 1 ] + 2 ];
for( INTNB i = 0; i < mp_nbVox[ 1 ] + 2; ++i ) mp_boundariesY[ i ] = -((HPFLTNB)(mp_halfFOV[ 1 ])) + ((HPFLTNB)(mp_sizeVox[ 1 ])) * ( ((HPFLTNB)i) - 0.5 );
mp_boundariesZ = new HPFLTNB[ mp_nbVox[ 2 ] + 2 ];
for( INTNB i = 0; i < mp_nbVox[ 2 ] + 2; ++i ) mp_boundariesZ[ i ] = -((HPFLTNB)(mp_halfFOV[ 2 ])) + ((HPFLTNB)(mp_sizeVox[ 2 ])) * ( ((HPFLTNB)i) - 0.5 );
// Allocating the mask buffer for the padded image space
INTNB nElts = ( mp_nbVox[ 0 ] + 2 ) * ( mp_nbVox[ 1 ] + 2 ) * ( mp_nbVox[ 2 ] + 2 );
mp_maskPad = new uint8_t[ nElts ];
::memset( mp_maskPad, 0, sizeof( uint8_t ) * nElts );
for( INTNB k = 1; k < mp_nbVox[ 2 ] + 1; ++k )
{
for( INTNB j = 1; j < mp_nbVox[ 1 ] + 1; ++j )
{
for( INTNB i = 1; i < mp_nbVox[ 0 ] + 1; ++i )
{
mp_maskPad[ i + j * ( ( mp_nbVox[ 0 ] + 2 ) ) + k * ( mp_nbVox[ 0 ] + 2 ) * ( mp_nbVox[ 1 ] + 2 ) ] = 1;
}
}
}
// Set the tolerance with respect to voxel sizes in each dimensions
HPFLTNB tolerance_factor = 1.e-6;
m_toleranceX = ((HPFLTNB)(mp_sizeVox[0])) * tolerance_factor;
m_toleranceY = ((HPFLTNB)(mp_sizeVox[1])) * tolerance_factor;
m_toleranceZ = ((HPFLTNB)(mp_sizeVox[2])) * tolerance_factor;
// Setting the bounds
m_boundX = (-mp_halfFOV[ 0 ]) - mp_sizeVox[ 0 ] * 0.5;
m_boundY = (-mp_halfFOV[ 1 ]) - mp_sizeVox[ 1 ] * 0.5;
m_boundZ = (-mp_halfFOV[ 2 ]) - mp_sizeVox[ 2 ] * 0.5;
// Normal end
return 0;
}
// =====================================================================
// ---------------------------------------------------------------------
// ---------------------------------------------------------------------
// =====================================================================
INTNB iProjectorJoseph::EstimateMaxNumberOfVoxelsPerLine()
{
// Find the maximum number of voxels along a given dimension
INTNB max_nb_voxels_in_dimension = mp_ImageDimensionsAndQuantification->GetNbVoxX();
if (mp_ImageDimensionsAndQuantification->GetNbVoxY()>max_nb_voxels_in_dimension) max_nb_voxels_in_dimension = mp_ImageDimensionsAndQuantification->GetNbVoxY();
if (mp_ImageDimensionsAndQuantification->GetNbVoxZ()>max_nb_voxels_in_dimension) max_nb_voxels_in_dimension = mp_ImageDimensionsAndQuantification->GetNbVoxZ();
// We should have at most 4 voxels in a given plane, so multiply by 4
max_nb_voxels_in_dimension *= 4;
// Return the value
return max_nb_voxels_in_dimension;
}
// =====================================================================
// ---------------------------------------------------------------------
// ---------------------------------------------------------------------
// =====================================================================
int iProjectorJoseph::ProjectWithoutTOF(int a_direction, oProjectionLine* ap_ProjectionLine )
{
#ifdef CASTOR_DEBUG
if (!m_initialized)
{
Cerr("***** iProjectorJoseph::ProjectWithoutTOF() -> Called while not initialized !" << endl);
Exit(EXIT_DEBUG);
}
#endif
#ifdef CASTOR_VERBOSE
if (m_verbose>=10)
{
string direction = "";
if (a_direction==FORWARD) direction = "forward";
else direction = "backward";
Cout("iProjectorJoseph::Project without TOF -> Project line '" << ap_ProjectionLine << "' in " << direction << " direction" << endl);
}
#endif
// Get event positions
FLTNB* event1 = ap_ProjectionLine->GetPosition1();
FLTNB* event2 = ap_ProjectionLine->GetPosition2();
// Distance between point event1 and event2
HPFLTNB const r[ 3 ] = {
event2[ 0 ] - event1[ 0 ],
event2[ 1 ] - event1[ 1 ],
event2[ 2 ] - event1[ 2 ]
};
// Square of r
HPFLTNB const r2[ 3 ] = {
r[ 0 ] * r[ 0 ],
r[ 1 ] * r[ 1 ],
r[ 2 ] * r[ 2 ]
};
// Find the first and last intersecting plane in X using the parametric
// values alphaMin and alphaMax
HPFLTNB alphaMin = 0.0, alphaMax = 1.0;
// Variables for Joseph
HPFLTNB pos[ 3 ] = { 0.0, 0.0, 0.0 }; // Position of point in image space
HPFLTNB wfl[ 2 ] = { 0.0, 0.0 }; // Weight floor
HPFLTNB wcl[ 2 ] = { 0.0, 0.0 }; // Weight ceil
HPFLTNB w[ 4 ] = { 0.0, 0.0, 0.0, 0.0 }; // Interpolation weight
int16_t index[ 2 ] = { 0, 0 }; // index in the image space
int32_t finalIdx = 0; // final index
int8_t limitX1 = 1; int8_t limitX2 = 1;
int8_t limitY1 = 1; int8_t limitY2 = 1;
int8_t limitZ1 = 1; int8_t limitZ2 = 1;
// Condition on the largest component of r
if( ::fabs( r[ 0 ] ) > ::fabs( r[ 1 ] ) )
{
// Computing the parametric values
// For the X-axis
// We stay in the limit of the image space
HPFLTNB const alphaX_0 = ( -mp_halfFOV[ 0 ] - event1[ 0 ] ) / r[ 0 ];
HPFLTNB const alphaX_1 = ( mp_halfFOV[ 0 ] - event1[ 0 ] ) / r[ 0 ];
alphaMin = std::max( alphaMin, std::min( alphaX_0, alphaX_1 ) );
alphaMax = std::min( alphaMax, std::max( alphaX_0, alphaX_1 ) );
// For the Y-axis
// We introduce 1 voxel size around Y-axis
// Make a shift on first and last plane (like an offset)
if( r[ 1 ] != 0.0 )
{
HPFLTNB const alphaY_0 = ( -mp_halfFOV[ 1 ] - mp_sizeVox[ 1 ] * 0.5 - event1[ 1 ] )
/ r[ 1 ];
HPFLTNB const alphaY_1 = ( mp_halfFOV[ 1 ] + mp_sizeVox[ 1 ] * 0.5 - event1[ 1 ] )
/ r[ 1 ];
alphaMin = std::max( alphaMin, std::min( alphaY_0, alphaY_1 ) );
alphaMax = std::min( alphaMax, std::max( alphaY_0, alphaY_1 ) );
}
// For the Z-axis
// We introduce 1 voxel size around Z-axis
// Make a shift on first and last plane (like an offset)
if( r[ 2 ] != 0.0 )
{
HPFLTNB const alphaZ_0 = ( -mp_halfFOV[ 2 ] - mp_sizeVox[ 2 ] * 0.5 - event1[ 2 ] )
/ r[ 2 ];
HPFLTNB const alphaZ_1 = ( mp_halfFOV[ 2 ] + mp_sizeVox[ 2 ] * 0.5 - event1[ 2 ] )
/ r[ 2 ];
alphaMin = std::max( alphaMin, std::min( alphaZ_0, alphaZ_1 ) );
alphaMax = std::min( alphaMax, std::max( alphaZ_0, alphaZ_1 ) );
}
if( alphaMax <= alphaMin ) return 0;
if( r[ 1 ] == 0.0 ) if( event1[ 1 ] < -mp_halfFOV[ 1 ] || event1[ 1 ] > mp_halfFOV[ 1 ] ) return 0;
if( r[ 2 ] == 0.0 ) if( event1[ 2 ] < -mp_halfFOV[ 2 ] || event1[ 2 ] > mp_halfFOV[ 2 ] ) return 0;
// Computing weight of normalization
HPFLTNB const factor( ::fabs( r[ 0 ] )
/ ::sqrt( r2[ 0 ] + r2[ 1 ] + r2[ 2 ] ) );
HPFLTNB const weight( mp_sizeVox[ 0 ] / factor );
// Computing the increment
HPFLTNB const ri[ 3 ] = {
1.0, // r[ 0 ] / r[ 0 ]
r[ 1 ] / r[ 0 ],
r[ 2 ] / r[ 0 ]
};
// Computing the first and the last plane
int iMin = 0, iMax = 0;
if( r[ 0 ] > 0.0 )
{
iMin = ::ceil( mp_nbVox[ 0 ] - m_toleranceX - ( mp_halfFOV[ 0 ] - alphaMin * r[ 0 ] - event1[ 0 ] ) / mp_sizeVox[ 0 ] );
iMax = ::floor( m_toleranceX + ( event1[ 0 ] + alphaMax * r[ 0 ] - (-mp_halfFOV[ 0 ]) ) / mp_sizeVox[ 0 ] );
}
else if( r[ 0 ] < 0.0 )
{
iMin = ::ceil( mp_nbVox[ 0 ] - m_toleranceX - ( mp_halfFOV[ 0 ] - alphaMax * r[ 0 ] - event1[ 0 ] ) / mp_sizeVox[ 0 ] );
iMax = ::floor( m_toleranceX + ( event1[ 0 ] + alphaMin * r[ 0 ] - (-mp_halfFOV[ 0 ]) ) / mp_sizeVox[ 0 ] );
}
// Computing an offset to get the correct position in plane
HPFLTNB const offset = -mp_halfFOV[ 0 ] + ( mp_sizeVox[ 0 ] * 0.5 ) - event1[ 0 ];
// Loop over all the crossing planes
for( int i = iMin; i < iMax; ++i )
{
// Computing position on crossed plane in term of grid spacing
HPFLTNB const step = offset + i * mp_sizeVox[ 0 ];
pos[ 1 ] = event1[ 1 ] + step * ri[ 1 ];
pos[ 2 ] = event1[ 2 ] + step * ri[ 2 ];
// Find the index in the image
index[ 0 ] = ::floor( m_toleranceY + ( pos[ 1 ] - m_boundY ) / mp_sizeVox[ 1 ] );
index[ 1 ] = ::floor( m_toleranceZ + ( pos[ 2 ] - m_boundZ ) / mp_sizeVox[ 2 ] );
// Storing values and indices
finalIdx = i + ( index[ 0 ] - 1 ) * mp_nbVox[ 0 ] + ( index[ 1 ] - 1 ) * m_nbVoxXY;
// Bilinear interpolation component (using floor)
wfl[ 0 ] = pos[ 1 ] - ( m_boundY + index[ 0 ] * mp_sizeVox[ 1 ] );
wfl[ 1 ] = pos[ 2 ] - ( m_boundZ + index[ 1 ] * mp_sizeVox[ 2 ] );
wfl[ 0 ] /= mp_sizeVox[ 1 ];
wfl[ 1 ] /= mp_sizeVox[ 2 ];
// Bilinear interpolation component (using ceil)
wcl[ 0 ] = 1.0 - wfl[ 0 ];
wcl[ 1 ] = 1.0 - wfl[ 1 ];
// Final coefficients
w[ 0 ] = wcl[ 0 ] * wcl[ 1 ];
w[ 1 ] = wcl[ 0 ] * wfl[ 1 ];
w[ 2 ] = wfl[ 0 ] * wfl[ 1 ];
w[ 3 ] = wfl[ 0 ] * wcl[ 1 ];
// Check Y bounds
if( index[ 0 ] <= 0 ) limitY1 = 0;
if( index[ 0 ] >= mp_nbVox[ 1 ] ) limitY2 = 0;
// Check Z bounds
if( index[ 1 ] <= 0 ) limitZ1 = 0;
if( index[ 1 ] >= mp_nbVox[ 2 ] ) limitZ2 = 0;
if (!m_hasMask || (limitY1 && limitZ1 && index[ 0 ] <= mp_nbVox[ 1 ] && index[ 1 ] <= mp_nbVox[ 2 ] && mp_mask[finalIdx]))
{
ap_ProjectionLine->AddVoxel(a_direction, finalIdx * limitY1 * limitZ1, w[ 0 ] * weight * limitY1 * limitZ1);
ap_ProjectionLine->AddVoxel(a_direction, ( finalIdx + m_nbVoxXY ) * limitY1 * limitZ2, w[ 1 ] * weight * limitY1 * limitZ2);
ap_ProjectionLine->AddVoxel(a_direction, ( finalIdx + mp_nbVox[ 0 ] + m_nbVoxXY ) * limitY2 * limitZ2, w[ 2 ] * weight * limitY2 * limitZ2);
ap_ProjectionLine->AddVoxel(a_direction, ( finalIdx + mp_nbVox[ 0 ] ) * limitY2 * limitZ1, w[ 3 ] * weight * limitY2 * limitZ1);
}
limitY1 = 1; limitY2 = 1;
limitZ1 = 1; limitZ2 = 1;
}
}
else
{
// Computing the parametric values
// For the X-axis
// We introduce 1 voxel size around Y-axis
if( r[ 0 ] != 0.0 )
{
HPFLTNB const alphaX_0 = ( -mp_halfFOV[ 0 ] - mp_sizeVox[ 0 ] * 0.5 - event1[ 0 ] )
/ r[ 0 ];
HPFLTNB const alphaX_1 = ( mp_halfFOV[ 0 ] + mp_sizeVox[ 0 ] * 0.5 - event1[ 0 ] )
/ r[ 0 ];
alphaMin = std::max( alphaMin, std::min( alphaX_0, alphaX_1 ) );
alphaMax = std::min( alphaMax, std::max( alphaX_0, alphaX_1 ) );
}
// For the Y-axis
// Make a shift on first and last plane (like an offset)
// We stay in the limit of the image space
HPFLTNB const alphaY_0 = ( -mp_halfFOV[ 1 ] - event1[ 1 ] ) / r[ 1 ];
HPFLTNB const alphaY_1 = ( mp_halfFOV[ 1 ] - event1[ 1 ] ) / r[ 1 ];
alphaMin = std::max( alphaMin, std::min( alphaY_0, alphaY_1 ) );
alphaMax = std::min( alphaMax, std::max( alphaY_0, alphaY_1 ) );
// For the Z-axis
// We introduce 1 voxel size around Z-axis
// Make a shift on first and last plane (like an offset)
if( r[ 2 ] != 0.0 )
{
HPFLTNB const alphaZ_0 = ( -mp_halfFOV[ 2 ] - mp_sizeVox[ 2 ] * 0.5 - event1[ 2 ] )
/ r[ 2 ];
HPFLTNB const alphaZ_1 = ( mp_halfFOV[ 2 ] + mp_sizeVox[ 2 ] * 0.5 - event1[ 2 ] )
/ r[ 2 ];
alphaMin = std::max( alphaMin, std::min( alphaZ_0, alphaZ_1 ) );
alphaMax = std::min( alphaMax, std::max( alphaZ_0, alphaZ_1 ) );
}
if( alphaMax <= alphaMin ) return 0;
if( r[ 0 ] == 0.0 ) if( event1[ 0 ] < -mp_halfFOV[ 0 ] || event1[ 0 ] > mp_halfFOV[ 0 ] ) return 0;
if( r[ 2 ] == 0.0 ) if( event1[ 2 ] < -mp_halfFOV[ 2 ] || event1[ 2 ] > mp_halfFOV[ 2 ] ) return 0;
// Computing weight of normalization
HPFLTNB const factor( ::fabs( r[ 1 ] ) / ::sqrt( r2[ 0 ] + r2[ 1 ] + r2[ 2 ] ) );
HPFLTNB const weight( mp_sizeVox[ 1 ] / factor );
// Computing the increment
HPFLTNB const ri[ 3 ] = {
r[ 0 ] / r[ 1 ],
1.0, // r[ 1 ] / r[ 1 ]
r[ 2 ] / r[ 1 ]
};
// Computing the first and the last plane
int jMin = 0, jMax = 0;
if( r[ 1 ] > 0.0 )
{
jMin = ::ceil( mp_nbVox[ 1 ] - m_toleranceY - ( mp_halfFOV[ 1 ] - alphaMin * r[ 1 ] - event1[ 1 ] ) / mp_sizeVox[ 1 ] );
jMax = ::floor( m_toleranceY + ( event1[ 1 ] + alphaMax * r[ 1 ] - (-mp_halfFOV[ 1 ]) ) / mp_sizeVox[ 1 ] );
}
else if( r[ 1 ] < 0.0 )
{
jMin = ::ceil( mp_nbVox[ 1 ] - m_toleranceY - ( mp_halfFOV[ 1 ] - alphaMax * r[ 1 ] - event1[ 1 ] ) / mp_sizeVox[ 1 ] );
jMax = ::floor( m_toleranceY + ( event1[ 1 ] + alphaMin * r[ 1 ] - (-mp_halfFOV[ 1 ]) ) / mp_sizeVox[ 1 ] );
}
// Computing an offset to get the correct position in plane
HPFLTNB const offset = -mp_halfFOV[ 1 ] + ( mp_sizeVox[ 1 ] * 0.5 ) - event1[ 1 ];
// Loop over all the crossing planes
for( int j = jMin; j < jMax; ++j )
{
// Computing position on crossed plane in term of grid spacing
HPFLTNB const step = offset + j * mp_sizeVox[ 1 ];
pos[ 0 ] = event1[ 0 ] + step * ri[ 0 ];
pos[ 2 ] = event1[ 2 ] + step * ri[ 2 ];
// Find the index in the image
index[ 0 ] = ::floor( m_toleranceX + ( pos[ 0 ] - m_boundX ) / mp_sizeVox[ 0 ] );
index[ 1 ] = ::floor( m_toleranceZ + ( pos[ 2 ] - m_boundZ ) / mp_sizeVox[ 2 ] );
// Storing values and indices
finalIdx = ( index[ 0 ] - 1 ) + j * mp_nbVox[ 0 ] + ( index[ 1 ] - 1 ) * m_nbVoxXY;
// Bilinear interpolation component (using floor)
wfl[ 0 ] = pos[ 0 ] - ( m_boundX + index[ 0 ] * mp_sizeVox[ 0 ] );
wfl[ 1 ] = pos[ 2 ] - ( m_boundZ + index[ 1 ] * mp_sizeVox[ 2 ] );
wfl[ 0 ] /= mp_sizeVox[ 0 ];
wfl[ 1 ] /= mp_sizeVox[ 2 ];
// Bilinear interpolation component (using ceil)
wcl[ 0 ] = 1.0 - wfl[ 0 ];
wcl[ 1 ] = 1.0 - wfl[ 1 ];
// Final coefficients
w[ 0 ] = wcl[ 0 ] * wcl[ 1 ];
w[ 1 ] = wcl[ 0 ] * wfl[ 1 ];
w[ 2 ] = wfl[ 0 ] * wfl[ 1 ];
w[ 3 ] = wfl[ 0 ] * wcl[ 1 ];
// Check X bounds
if( index[ 0 ] <= 0 ) limitX1 = 0;
if( index[ 0 ] >= mp_nbVox[ 0 ] ) limitX2 = 0;
// Check Z bounds
if( index[ 1 ] <= 0 ) limitZ1 = 0;
if( index[ 1 ] >= mp_nbVox[ 2 ] ) limitZ2 = 0;
if (!m_hasMask || (limitX1 && limitZ1 && index[ 0 ] <= mp_nbVox[ 0 ] && index[ 1 ] <= mp_nbVox[ 2 ] && mp_mask[finalIdx]))
{
ap_ProjectionLine->AddVoxel(a_direction, finalIdx * limitX1 * limitZ1, w[ 0 ] * weight * limitX1 * limitZ1);
ap_ProjectionLine->AddVoxel(a_direction, ( finalIdx + m_nbVoxXY ) * limitX1 * limitZ2, w[ 1 ] * weight * limitX1 * limitZ2);
ap_ProjectionLine->AddVoxel(a_direction, ( finalIdx + 1 + m_nbVoxXY ) * limitX2 * limitZ2, w[ 2 ] * weight * limitX2 * limitZ2);
ap_ProjectionLine->AddVoxel(a_direction, ( finalIdx + 1 ) * limitX2 * limitZ1, w[ 3 ] * weight * limitX2 * limitZ1);
}
limitX1 = 1; limitX2 = 1;
limitZ1 = 1; limitZ2 = 1;
}
}
return 0;
}
// =====================================================================
// ---------------------------------------------------------------------
// ---------------------------------------------------------------------
// =====================================================================
int iProjectorJoseph::ProjectTOFListmode(int a_direction, oProjectionLine* ap_ProjectionLine)
{
#ifdef CASTOR_DEBUG
if (!m_initialized)
{
Cerr("***** iProjectorJoseph::ProjectTOFListmode() -> Called while not initialized !" << endl);
Exit(EXIT_DEBUG);
}
#endif
#ifdef CASTOR_VERBOSE
if (m_verbose>=10)
{
string direction = "";
if (a_direction==FORWARD) direction = "forward";
else direction = "backward";
Cout("iProjectorJoseph::Project with TOF measurement -> Project line '" << ap_ProjectionLine << "' in " << direction << " direction" << endl);
}
#endif
// Get event positions
FLTNB* event1 = ap_ProjectionLine->GetPosition1();
FLTNB* event2 = ap_ProjectionLine->GetPosition2();
// Distance between point event1 and event2
HPFLTNB const r[ 3 ] = {
event2[ 0 ] - event1[ 0 ],
event2[ 1 ] - event1[ 1 ],
event2[ 2 ] - event1[ 2 ]
};
/*
// Square of r
HPFLTNB const r2[ 3 ] = {
r[ 0 ] * r[ 0 ],
r[ 1 ] * r[ 1 ],
r[ 2 ] * r[ 2 ]
};
*/
// LOR length
HPFLTNB lor_length = ap_ProjectionLine->GetLength();
// Get TOF info
FLTNB tof_measurement = ap_ProjectionLine->GetTOFMeasurementInPs();
// convert delta time into delta length
HPFLTNB tof_delta = tof_measurement * SPEED_OF_LIGHT_IN_MM_PER_PS * 0.5;
// TOF standard deviation and truncation
HPFLTNB tof_sigma = m_TOFResolutionInMm / TWO_SQRT_TWO_LN_2;
HPFLTNB tof_sigma_sqrt2 = sqrt(2.)*tof_sigma;
HPFLTNB tof_half_span = 0.;
if (m_TOFWeightingFcnPrecomputedFlag) tof_half_span = ((HPFLTNB)m_TOFWeightingFcnNbSamples)/(2.*m_TOFPrecomputedSamplingFactor);
else if (m_TOFBinProperProcessingFlag) tof_half_span = tof_sigma * m_TOFNbSigmas + 0.5 * m_TOFBinSizeInMm;
else tof_half_span = tof_sigma * m_TOFNbSigmas;
// distance between the first event1 and the TOF measurement
HPFLTNB lor_tof_center = lor_length * 0.5 + tof_delta;
// coordinates of the lower edge of the first voxel falling inside the truncated TOF distribution centered at the TOF measurement
HPFLTNB tof_edge_low[] = {0,0,0};
// coordinate of the upper edge of the last voxel falling inside the truncated TOF distribution centered at the TOF measurement
HPFLTNB tof_edge_high[] = {0,0,0};
HPFLTNB tof_center;
INTNB tof_index;
// low/high voxel edges (in absolute coordinates) for truncated TOF
for (int ax=0;ax<3;ax++)
{
// absolute coordinate along each axis of the center of the TOF distribution
tof_center = event1[ax] + lor_tof_center * r[ax] / lor_length;
// absolute coordinate along each axis of the lowest voxel edge spanned by the TOF distribution, limited by the lowest FOV edge
tof_edge_low[ax] = tof_center - tof_half_span * fabs(r[ax]) / lor_length;
tof_index = max( (INTNB)::floor( (tof_edge_low[ax] - (-mp_halfFOV[ax])) / mp_sizeVox[ax] ), (INTNB)0);
// if low TOF edge above the highest FOV edge, return empty line
if (tof_index>mp_nbVox[ax]-1) return 0;
tof_edge_low[ax] = (HPFLTNB)tof_index * mp_sizeVox[ax] - mp_halfFOV[ax];
// absolute coordinate along each axis of the highest voxel edge spanned by the TOF distribution, limited by the highest FOV edge
tof_edge_high[ax] = tof_center + tof_half_span * fabs(r[ax]) / lor_length;
tof_index = min( (INTNB)::floor( (tof_edge_high[ax] - (-mp_halfFOV[ax])) / mp_sizeVox[ax] ), mp_nbVox[ax]-1);
// if high TOF edge below the lowest FOV edge, return empty line
if (tof_index<0) return 0;
tof_edge_high[ax] = (HPFLTNB)(tof_index+1) * mp_sizeVox[ax] - mp_halfFOV[ax];
}
// Find the first and last intersecting plane in X using the parametric
// values alphaMin and alphaMax
HPFLTNB alphaMin = 0.0, alphaMax = 1.0;
// Variables for Joseph
HPFLTNB pos[ 3 ] = { 0.0, 0.0, 0.0 }; // Position of point in image space
HPFLTNB wfl[ 2 ] = { 0.0, 0.0 }; // Weight floor
HPFLTNB wcl[ 2 ] = { 0.0, 0.0 }; // Weight ceil
HPFLTNB w[ 4 ] = { 0.0, 0.0, 0.0, 0.0 }; // Interpolation weight
int16_t index[ 2 ] = { 0, 0 }; // index in the image space
int32_t finalIdx = 0; // final index
int8_t limitX1 = 1; int8_t limitX2 = 1;
int8_t limitY1 = 1; int8_t limitY2 = 1;
int8_t limitZ1 = 1; int8_t limitZ2 = 1;
// Condition on the largest component of r
if( ::fabs( r[ 0 ] ) > ::fabs( r[ 1 ] ) )
{
// Computing the parametric values
// For the X-axis
// We stay in the limit of the image space
HPFLTNB const alphaX_0 = ( tof_edge_low[ 0 ] - event1[ 0 ] ) / r[ 0 ];
HPFLTNB const alphaX_1 = ( tof_edge_high[ 0 ] - event1[ 0 ] ) / r[ 0 ];
alphaMin = std::max( alphaMin, std::min( alphaX_0, alphaX_1 ) );
alphaMax = std::min( alphaMax, std::max( alphaX_0, alphaX_1 ) );
// For the Y-axis
// We introduce 1 voxel size around Y-axis
// Make a shift on first and last plane (like an offset)
if( r[ 1 ] != 0.0 )
{
HPFLTNB const alphaY_0 = ( tof_edge_low[ 1 ] - mp_sizeVox[ 1 ] * 0.5 - event1[ 1 ] )
/ r[ 1 ];
HPFLTNB const alphaY_1 = ( tof_edge_high[ 1 ] + mp_sizeVox[ 1 ] * 0.5 - event1[ 1 ] )
/ r[ 1 ];
alphaMin = std::max( alphaMin, std::min( alphaY_0, alphaY_1 ) );
alphaMax = std::min( alphaMax, std::max( alphaY_0, alphaY_1 ) );
}
// For the Z-axis
// We introduce 1 voxel size around Z-axis
// Make a shift on first and last plane (like an offset)
if( r[ 2 ] != 0.0 )
{
HPFLTNB const alphaZ_0 = ( tof_edge_low[ 2 ] - mp_sizeVox[ 2 ] * 0.5 - event1[ 2 ] )
/ r[ 2 ];
HPFLTNB const alphaZ_1 = ( tof_edge_high[ 2 ] + mp_sizeVox[ 2 ] * 0.5 - event1[ 2 ] )
/ r[ 2 ];
alphaMin = std::max( alphaMin, std::min( alphaZ_0, alphaZ_1 ) );
alphaMax = std::min( alphaMax, std::max( alphaZ_0, alphaZ_1 ) );
}
if( alphaMax <= alphaMin ) return 0;
if( r[ 1 ] == 0.0 ) if( event1[ 1 ] < -mp_halfFOV[ 1 ] || event1[ 1 ] > mp_halfFOV[ 1 ] ) return 0;
if( r[ 2 ] == 0.0 ) if( event1[ 2 ] < -mp_halfFOV[ 2 ] || event1[ 2 ] > mp_halfFOV[ 2 ] ) return 0;
// Computing weight of normalization
HPFLTNB const factor( ::fabs( r[ 0 ] ) / lor_length );
HPFLTNB const factor_for_tof( lor_length / r[ 0 ]) ;
HPFLTNB const weight( mp_sizeVox[ 0 ] / factor );
// Computing the increment
HPFLTNB const ri[ 3 ] = {
1.0, // r[ 0 ] / r[ 0 ]
r[ 1 ] / r[ 0 ],
r[ 2 ] / r[ 0 ]
};
// Computing the first and the last plane
int iMin = 0, iMax = 0;
if( r[ 0 ] > 0.0 )
{
iMin = ::ceil( mp_nbVox[ 0 ] - m_toleranceX - ( mp_halfFOV[ 0 ] - alphaMin * r[ 0 ] - event1[ 0 ] ) / mp_sizeVox[ 0 ] );
iMax = ::floor( m_toleranceX + ( event1[ 0 ] + alphaMax * r[ 0 ] - (-mp_halfFOV[ 0 ]) ) / mp_sizeVox[ 0 ] );
}
else if( r[ 0 ] < 0.0 )
{
iMin = ::ceil( mp_nbVox[ 0 ] - m_toleranceX - ( mp_halfFOV[ 0 ] - alphaMax * r[ 0 ] - event1[ 0 ] ) / mp_sizeVox[ 0 ] );
iMax = ::floor( m_toleranceX + ( event1[ 0 ] + alphaMin * r[ 0 ] - (-mp_halfFOV[ 0 ]) ) / mp_sizeVox[ 0 ] );
}
// Computing an offset to get the correct position in plane
HPFLTNB const offset = -mp_halfFOV[ 0 ] + ( mp_sizeVox[ 0 ] * 0.5 ) - event1[ 0 ];
//// tof : erf of previous voxel edge - Gaussian center
//HPFLTNB previous_edge_erf = erf( ( ( -mp_halfFOV[ 0 ] + iMin * mp_sizeVox[ 0 ] - event1[ 0 ] ) * factor_for_tof - lor_tof_center) / tof_sigma_sqrt2 );
//HPFLTNB next_edge_erf = 0.;
HPFLTNB tof_weight = 0.;
// Loop over all the crossing planes
for( int i = iMin; i < iMax; ++i )
{
// Computing position on crossed plane in term of grid spacing
HPFLTNB const step = offset + i * mp_sizeVox[ 0 ];
pos[ 1 ] = event1[ 1 ] + step * ri[ 1 ];
pos[ 2 ] = event1[ 2 ] + step * ri[ 2 ];
// Find the index in the image
index[ 0 ] = ::floor( m_toleranceY + ( pos[ 1 ] - m_boundY ) / mp_sizeVox[ 1 ] );
index[ 1 ] = ::floor( m_toleranceZ + ( pos[ 2 ] - m_boundZ ) / mp_sizeVox[ 2 ] );
// Bilinear interpolation component (using floor)
wfl[ 0 ] = pos[ 1 ] - ( m_boundY + index[ 0 ] * mp_sizeVox[ 1 ] );
wfl[ 1 ] = pos[ 2 ] - ( m_boundZ + index[ 1 ] * mp_sizeVox[ 2 ] );
wfl[ 0 ] /= mp_sizeVox[ 1 ];
wfl[ 1 ] /= mp_sizeVox[ 2 ];
// Bilinear interpolation component (using ceil)
wcl[ 0 ] = 1.0 - wfl[ 0 ];
wcl[ 1 ] = 1.0 - wfl[ 1 ];
// Final coefficients
w[ 0 ] = wcl[ 0 ] * wcl[ 1 ];
w[ 1 ] = wcl[ 0 ] * wfl[ 1 ];
w[ 2 ] = wfl[ 0 ] * wfl[ 1 ];
w[ 3 ] = wfl[ 0 ] * wcl[ 1 ];
// Check Y bounds
if( index[ 0 ] <= 0 ) limitY1 = 0;
if( index[ 0 ] >= mp_nbVox[ 1 ] ) limitY2 = 0;
// Check Z bounds
if( index[ 1 ] <= 0 ) limitZ1 = 0;
if( index[ 1 ] >= mp_nbVox[ 2 ] ) limitZ2 = 0;
// Storing values and indices
finalIdx = i + ( index[ 0 ] - 1 ) * mp_nbVox[ 0 ] + ( index[ 1 ] - 1 ) * m_nbVoxXY;
tof_weight = 0.;
if (m_TOFWeightingFcnPrecomputedFlag)
{
// fetch the value of the precomputed TOF weighting function (centered at the TOF measurement)
// nearest to the projection of the voxel center onto the LOR
INTNB temp = m_TOFWeightingFcnNbSamples/2 + (INTNB)round( ( step * factor_for_tof - lor_tof_center ) * m_TOFPrecomputedSamplingFactor );
if (temp>=0 && tempAddVoxel(a_direction, finalIdx * limitY1 * limitZ1, w[ 0 ] * weight * tof_weight * limitY1 * limitZ1);
ap_ProjectionLine->AddVoxel(a_direction, ( finalIdx + m_nbVoxXY ) * limitY1 * limitZ2, w[ 1 ] * weight * tof_weight * limitY1 * limitZ2);
ap_ProjectionLine->AddVoxel(a_direction, ( finalIdx + mp_nbVox[ 0 ] + m_nbVoxXY ) * limitY2 * limitZ2, w[ 2 ] * weight * tof_weight * limitY2 * limitZ2);
ap_ProjectionLine->AddVoxel(a_direction, ( finalIdx + mp_nbVox[ 0 ] ) * limitY2 * limitZ1, w[ 3 ] * weight * tof_weight * limitY2 * limitZ1);
}
limitY1 = 1.0; limitY2 = 1.0;
limitZ1 = 1.0; limitZ2 = 1.0;
}
}
else
{
// Computing the parametric values
// For the X-axis
// We introduce 1 voxel size around Y-axis
if( r[ 0 ] != 0.0 )
{
HPFLTNB const alphaX_0 = ( tof_edge_low[ 0 ] - mp_sizeVox[ 0 ] * 0.5 - event1[ 0 ] )
/ r[ 0 ];
HPFLTNB const alphaX_1 = ( tof_edge_high[ 0 ] + mp_sizeVox[ 0 ] * 0.5 - event1[ 0 ] )
/ r[ 0 ];
alphaMin = std::max( alphaMin, std::min( alphaX_0, alphaX_1 ) );
alphaMax = std::min( alphaMax, std::max( alphaX_0, alphaX_1 ) );
}
// For the Y-axis
// Make a shift on first and last plane (like an offset)
// We stay in the limit of the image space
HPFLTNB const alphaY_0 = ( tof_edge_low[ 1 ] - event1[ 1 ] ) / r[ 1 ];
HPFLTNB const alphaY_1 = ( tof_edge_high[ 1 ] - event1[ 1 ] ) / r[ 1 ];
alphaMin = std::max( alphaMin, std::min( alphaY_0, alphaY_1 ) );
alphaMax = std::min( alphaMax, std::max( alphaY_0, alphaY_1 ) );
// For the Z-axis
// We introduce 1 voxel size around Z-axis
// Make a shift on first and last plane (like an offset)
if( r[ 2 ] != 0.0 )
{
HPFLTNB const alphaZ_0 = ( tof_edge_low[ 2 ] - mp_sizeVox[ 2 ] * 0.5 - event1[ 2 ] )
/ r[ 2 ];
HPFLTNB const alphaZ_1 = ( tof_edge_high[ 2 ] + mp_sizeVox[ 2 ] * 0.5 - event1[ 2 ] )
/ r[ 2 ];
alphaMin = std::max( alphaMin, std::min( alphaZ_0, alphaZ_1 ) );
alphaMax = std::min( alphaMax, std::max( alphaZ_0, alphaZ_1 ) );
}
if( alphaMax <= alphaMin ) return 0;
if( r[ 0 ] == 0.0 ) if( event1[ 0 ] < -mp_halfFOV[ 0 ] || event1[ 0 ] > mp_halfFOV[ 0 ] ) return 0;
if( r[ 2 ] == 0.0 ) if( event1[ 2 ] < -mp_halfFOV[ 2 ] || event1[ 2 ] > mp_halfFOV[ 2 ] ) return 0;
// Computing weight of normalization
HPFLTNB const factor( ::fabs( r[ 1 ] ) / lor_length );
HPFLTNB const factor_for_tof( lor_length / r[ 1 ] );
HPFLTNB const weight( mp_sizeVox[ 1 ] / factor );
// Computing the increment
HPFLTNB const ri[ 3 ] = {
r[ 0 ] / r[ 1 ],
1.0, // r[ 1 ] / r[ 1 ]
r[ 2 ] / r[ 1 ]
};
// Computing the first and the last plane
int jMin = 0, jMax = 0;
if( r[ 1 ] > 0.0 )
{
jMin = ::ceil( mp_nbVox[ 1 ] - m_toleranceY - ( mp_halfFOV[ 1 ] - alphaMin * r[ 1 ] - event1[ 1 ] ) / mp_sizeVox[ 1 ] );
jMax = ::floor( m_toleranceY + ( event1[ 1 ] + alphaMax * r[ 1 ] - (-mp_halfFOV[ 1 ]) ) / mp_sizeVox[ 1 ] );
}
else if( r[ 1 ] < 0.0 )
{
jMin = ::ceil( mp_nbVox[ 1 ] - m_toleranceY - ( mp_halfFOV[ 1 ] - alphaMax * r[ 1 ] - event1[ 1 ] ) / mp_sizeVox[ 1 ] );
jMax = ::floor( m_toleranceY + ( event1[ 1 ] + alphaMin * r[ 1 ] - (-mp_halfFOV[ 1 ]) ) / mp_sizeVox[ 1 ] );
}
//// tof : erf of previous voxel edge - Gaussian center
//HPFLTNB previous_edge_erf = erf( ( ( -mp_halfFOV[ 1 ] + jMin * mp_sizeVox[ 1 ] - event1[ 1 ]) * factor_for_tof - lor_tof_center) / tof_sigma_sqrt2 );
//HPFLTNB next_edge_erf = 0.;
HPFLTNB tof_weight = 0.;
// Computing an offset to get the correct position in plane
HPFLTNB const offset = -mp_halfFOV[ 1 ] + ( mp_sizeVox[ 1 ] * 0.5 ) - event1[ 1 ];
// Loop over all the crossing planes
for( int j = jMin; j < jMax; ++j )
{
// Computing position on crossed plane in term of grid spacing
HPFLTNB const step = offset + j * mp_sizeVox[ 1 ];
pos[ 0 ] = event1[ 0 ] + step * ri[ 0 ];
pos[ 2 ] = event1[ 2 ] + step * ri[ 2 ];
// Find the index in the image
index[ 0 ] = ::floor( m_toleranceX + ( pos[ 0 ] - m_boundX ) / mp_sizeVox[ 0 ] );
index[ 1 ] = ::floor( m_toleranceZ + ( pos[ 2 ] - m_boundZ ) / mp_sizeVox[ 2 ] );
// Bilinear interpolation component (using floor)
wfl[ 0 ] = pos[ 0 ] - ( m_boundX + index[ 0 ] * mp_sizeVox[ 0 ] );
wfl[ 1 ] = pos[ 2 ] - ( m_boundZ + index[ 1 ] * mp_sizeVox[ 2 ] );
wfl[ 0 ] /= mp_sizeVox[ 0 ];
wfl[ 1 ] /= mp_sizeVox[ 2 ];
// Bilinear interpolation component (using ceil)
wcl[ 0 ] = 1.0 - wfl[ 0 ];
wcl[ 1 ] = 1.0 - wfl[ 1 ];
// Final coefficients
w[ 0 ] = wcl[ 0 ] * wcl[ 1 ];
w[ 1 ] = wcl[ 0 ] * wfl[ 1 ];
w[ 2 ] = wfl[ 0 ] * wfl[ 1 ];
w[ 3 ] = wfl[ 0 ] * wcl[ 1 ];
// Check X bounds
if( index[ 0 ] <= 0 ) limitX1 = 0;
if( index[ 0 ] >= mp_nbVox[ 0 ] ) limitX2 = 0;
// Check Z bounds
if( index[ 1 ] <= 0 ) limitZ1 = 0;
if( index[ 1 ] >= mp_nbVox[ 2 ] ) limitZ2 = 0;
// Storing values and indices
finalIdx = ( index[ 0 ] - 1 ) + j * mp_nbVox[ 0 ] + ( index[ 1 ] - 1 ) * m_nbVoxXY;
tof_weight = 0.;
if (m_TOFWeightingFcnPrecomputedFlag)
{
// fetch the value of the precomputed TOF weighting function (centered at the TOF measurement)
// nearest to the projection of the voxel center onto the LOR
INTNB temp = m_TOFWeightingFcnNbSamples/2 + (INTNB)round( ( step * factor_for_tof - lor_tof_center) * m_TOFPrecomputedSamplingFactor );
if (temp>=0 && tempAddVoxel(a_direction, finalIdx * limitX1 * limitZ1, w[ 0 ] * weight * tof_weight * limitX1 * limitZ1);
ap_ProjectionLine->AddVoxel(a_direction, ( finalIdx + m_nbVoxXY ) * limitX1 * limitZ2, w[ 1 ] * weight * tof_weight * limitX1 * limitZ2);
ap_ProjectionLine->AddVoxel(a_direction, ( finalIdx + 1 + m_nbVoxXY ) * limitX2 * limitZ2, w[ 2 ] * weight * tof_weight * limitX2 * limitZ2);
ap_ProjectionLine->AddVoxel(a_direction, ( finalIdx + 1 ) * limitX2 * limitZ1, w[ 3 ] * weight * tof_weight * limitX2 * limitZ1);
}
limitX1 = 1; limitX2 = 1;
limitZ1 = 1; limitZ2 = 1;
}
}
return 0;
}
// =====================================================================
// ---------------------------------------------------------------------
// ---------------------------------------------------------------------
// =====================================================================
int iProjectorJoseph::ProjectTOFHistogram(int a_direction, oProjectionLine* ap_ProjectionLine)
{
#ifdef CASTOR_DEBUG
if (!m_initialized)
{
Cerr("***** iProjectorJoseph::ProjectTOFHistogram() -> Called while not initialized !" << endl);
Exit(EXIT_DEBUG);
}
#endif
#ifdef CASTOR_VERBOSE
if (m_verbose>=10)
{
string direction = "";
if (a_direction==FORWARD) direction = "forward";
else direction = "backward";
Cout("iProjectorJoseph::Project with TOF bins -> Project line '" << ap_ProjectionLine << "' in " << direction << " direction" << endl);
}
#endif
// Get event positions
FLTNB* event1 = ap_ProjectionLine->GetPosition1();
FLTNB* event2 = ap_ProjectionLine->GetPosition2();
// Distance between point event1 and event2
HPFLTNB const r[ 3 ] = {
event2[ 0 ] - event1[ 0 ],
event2[ 1 ] - event1[ 1 ],
event2[ 2 ] - event1[ 2 ]
};
// LOR length
HPFLTNB lor_length = ap_ProjectionLine->GetLength();
HPFLTNB lor_length_half = lor_length * 0.5;
// Get TOF info
INTNB tof_nb_bins = ap_ProjectionLine->GetNbTOFBins();
INTNB tof_half_nb_bins = tof_nb_bins/2;
// TOF Gaussian standard deviation and truncation
HPFLTNB tof_sigma = m_TOFResolutionInMm / TWO_SQRT_TWO_LN_2;
HPFLTNB tof_sigma_sqrt2 = sqrt(2.)*tof_sigma;
HPFLTNB tof_half_span = 0.;
if (m_TOFWeightingFcnPrecomputedFlag) tof_half_span = ((HPFLTNB)m_TOFWeightingFcnNbSamples)/(2.*m_TOFPrecomputedSamplingFactor);
else tof_half_span = tof_sigma * m_TOFNbSigmas;
// if integration of the Gaussian over the TOF bin, need for help variables to save calls to erf
HPFLTNB prev_erf = 0., new_erf = 0.;
// minimum and maximum TOF bins, the whole span
INTNB tof_bin_last = tof_half_nb_bins;
INTNB tof_bin_first = -tof_half_nb_bins;
// distance between the first event1 and the center of a TOF bin along the LOR
HPFLTNB lor_tof_center = 0.;
// the sum of all TOF bin weights for a voxel
//HPFLTNB tof_norm_coef = 0.;
// the first and the last relevant TOF bins for a voxel
INTNB tof_bin_first_for_voxel = 0, tof_bin_last_for_voxel = 0;
/*
// Square of r
HPFLTNB const r2[ 3 ] = {
r[ 0 ] * r[ 0 ],
r[ 1 ] * r[ 1 ],
r[ 2 ] * r[ 2 ]
};
*/
// Find the first and last intersecting plane in X using the parametric
// values alphaMin and alphaMax
HPFLTNB alphaMin = 0.0, alphaMax = 1.0;
// Variables for Joseph
HPFLTNB pos[ 3 ] = { 0.0, 0.0, 0.0 }; // Position of point in image space
HPFLTNB wfl[ 2 ] = { 0.0, 0.0 }; // Weight floor
HPFLTNB wcl[ 2 ] = { 0.0, 0.0 }; // Weight ceil
HPFLTNB w[ 4 ] = { 0.0, 0.0, 0.0, 0.0 }; // Interpolation weight
int16_t index[ 2 ] = { 0, 0 }; // index in the image space
int32_t finalIdx = 0; // final index
int8_t limitX1 = 1; int8_t limitX2 = 1;
int8_t limitY1 = 1; int8_t limitY2 = 1;
int8_t limitZ1 = 1; int8_t limitZ2 = 1;
// Condition on the largest component of r
if( ::fabs( r[ 0 ] ) > ::fabs( r[ 1 ] ) )
{
// Computing the parametric values
// For the X-axis
// We stay in the limit of the image space
HPFLTNB const alphaX_0 = ( -mp_halfFOV[ 0 ] - event1[ 0 ] ) / r[ 0 ];
HPFLTNB const alphaX_1 = ( mp_halfFOV[ 0 ] - event1[ 0 ] ) / r[ 0 ];
alphaMin = std::max( alphaMin, std::min( alphaX_0, alphaX_1 ) );
alphaMax = std::min( alphaMax, std::max( alphaX_0, alphaX_1 ) );
// For the Y-axis
// We introduce 1 voxel size around Y-axis
// Make a shift on first and last plane (like an offset)
if( r[ 1 ] != 0.0 )
{
HPFLTNB const alphaY_0 = ( -mp_halfFOV[ 1 ] - mp_sizeVox[ 1 ] * 0.5 - event1[ 1 ] )
/ r[ 1 ];
HPFLTNB const alphaY_1 = ( mp_halfFOV[ 1 ] + mp_sizeVox[ 1 ] * 0.5 - event1[ 1 ] )
/ r[ 1 ];
alphaMin = std::max( alphaMin, std::min( alphaY_0, alphaY_1 ) );
alphaMax = std::min( alphaMax, std::max( alphaY_0, alphaY_1 ) );
}
// For the Z-axis
// We introduce 1 voxel size around Z-axis
// Make a shift on first and last plane (like an offset)
if( r[ 2 ] != 0.0 )
{
HPFLTNB const alphaZ_0 = ( -mp_halfFOV[ 2 ] - mp_sizeVox[ 2 ] * 0.5 - event1[ 2 ] )
/ r[ 2 ];
HPFLTNB const alphaZ_1 = ( mp_halfFOV[ 2 ] + mp_sizeVox[ 2 ] * 0.5 - event1[ 2 ] )
/ r[ 2 ];
alphaMin = std::max( alphaMin, std::min( alphaZ_0, alphaZ_1 ) );
alphaMax = std::min( alphaMax, std::max( alphaZ_0, alphaZ_1 ) );
}
if( alphaMax <= alphaMin ) return 0;
if( r[ 1 ] == 0.0 ) if( event1[ 1 ] < -mp_halfFOV[ 1 ] || event1[ 1 ] > mp_halfFOV[ 1 ] ) return 0;
if( r[ 2 ] == 0.0 ) if( event1[ 2 ] < -mp_halfFOV[ 2 ] || event1[ 2 ] > mp_halfFOV[ 2 ] ) return 0;
// temporary storage for TOF bin weights
// allocation after potential returns
HPFLTNB* tof_weights_temp = new HPFLTNB[tof_nb_bins];
for (INTNB tof_bin=0; tof_bin 0.0 )
{
iMin = ::ceil( mp_nbVox[ 0 ] - m_toleranceX - ( mp_halfFOV[ 0 ] - alphaMin * r[ 0 ] - event1[ 0 ] ) / mp_sizeVox[ 0 ] );
iMax = ::floor( m_toleranceX + ( event1[ 0 ] + alphaMax * r[ 0 ] - (-mp_halfFOV[ 0 ]) ) / mp_sizeVox[ 0 ] );
}
else if( r[ 0 ] < 0.0 )
{
iMin = ::ceil( mp_nbVox[ 0 ] - m_toleranceX - ( mp_halfFOV[ 0 ] - alphaMax * r[ 0 ] - event1[ 0 ] ) / mp_sizeVox[ 0 ] );
iMax = ::floor( m_toleranceX + ( event1[ 0 ] + alphaMin * r[ 0 ] - (-mp_halfFOV[ 0 ]) ) / mp_sizeVox[ 0 ] );
}
// Computing an offset to get the correct position in plane
HPFLTNB const offset = -mp_halfFOV[ 0 ] + ( mp_sizeVox[ 0 ] * 0.5 ) - event1[ 0 ];
// Loop over all the crossing planes
for( int i = iMin; i < iMax; ++i )
{
// Computing position on crossed plane in term of grid spacing
HPFLTNB const step = offset + i * mp_sizeVox[ 0 ];
pos[ 1 ] = event1[ 1 ] + step * ri[ 1 ];
pos[ 2 ] = event1[ 2 ] + step * ri[ 2 ];
// Find the index in the image
index[ 0 ] = ::floor( m_toleranceY + ( pos[ 1 ] - m_boundY ) / mp_sizeVox[ 1 ] );
index[ 1 ] = ::floor( m_toleranceZ + ( pos[ 2 ] - m_boundZ ) / mp_sizeVox[ 2 ] );
finalIdx = i + ( index[ 0 ] - 1 ) * mp_nbVox[ 0 ] + ( index[ 1 ] - 1 ) * m_nbVoxXY;
// check if the main voxel is relevant
if (m_hasMask && index[ 0 ] > 0 && index[ 1 ] > 0 && index[ 0 ] <= mp_nbVox[ 1 ] && index[ 1 ] <= mp_nbVox[ 2 ] && !mp_mask[finalIdx]) continue;
// Compute the first and the last relevant TOF bin for this voxel
if (!m_TOFWeightingFcnPrecomputedFlag && m_TOFBinProperProcessingFlag)
{
// taking the TOF bins reached by the truncated Gaussian centered at the voxel center projection
tof_bin_first_for_voxel = max(tof_bin_first , (INTNB)(floor( (step * factor_for_tof - tof_half_span - lor_length_half - m_TOFBinSizeInMm/2.) / m_TOFBinSizeInMm )));
tof_bin_last_for_voxel = min(tof_bin_last , (INTNB)(ceil( (step * factor_for_tof + tof_half_span - lor_length_half + m_TOFBinSizeInMm/2.) / m_TOFBinSizeInMm )));
}
else
{
// taking the TOF bins whose TOF weighting function, centered at the bin center, reaches the voxel center projection
tof_bin_first_for_voxel = max(tof_bin_first , (INTNB)(ceil( (step * factor_for_tof - tof_half_span - lor_length_half ) / m_TOFBinSizeInMm )));
tof_bin_last_for_voxel = min(tof_bin_last , (INTNB)(floor( (step * factor_for_tof + tof_half_span - lor_length_half) / m_TOFBinSizeInMm )));
}
lor_tof_center = lor_length_half + tof_bin_first_for_voxel * m_TOFBinSizeInMm;
// shift tof bin indices from -/+ to 0:nbBins range
tof_bin_first_for_voxel += tof_half_nb_bins;
tof_bin_last_for_voxel += tof_half_nb_bins;
// initialization of help variables for reducing calls to erf
if (!m_TOFWeightingFcnPrecomputedFlag && m_TOFBinProperProcessingFlag)
{
// bound the integration to the Gaussian truncation
HPFLTNB temp = std::min(tof_half_span,std::max(-tof_half_span, lor_tof_center - m_TOFBinSizeInMm/2. - step * factor_for_tof));
prev_erf = erf(temp/tof_sigma_sqrt2);
}
// compute the TOF bin weights for the current voxel for all relevant TOF bins
//tof_norm_coef = 0.;
for (INTNB tof_bin=tof_bin_first_for_voxel; tof_bin<=tof_bin_last_for_voxel; tof_bin++)
{
if (m_TOFWeightingFcnPrecomputedFlag)
{
// fetch the value of the precomputed TOF weighting function (centered at the TOF bin center)
// nearest to the projection of the voxel center onto the LOR
INTNB temp = m_TOFWeightingFcnNbSamples/2 + (INTNB)round( (step * factor_for_tof - lor_tof_center) * m_TOFPrecomputedSamplingFactor);
if (temp>=0 && temp= mp_nbVox[ 1 ] ) limitY2 = 0;
// Check Z bounds
if( index[ 1 ] <= 0 ) limitZ1 = 0;
if( index[ 1 ] >= mp_nbVox[ 2 ] ) limitZ2 = 0;
/*
if (tof_norm_coef>0.)
{
// first normalize TOF bin weights so that they sum to 1
for (INTNB tof_bin=tof_bin_first_for_voxel; tof_bin<=tof_bin_last_for_voxel; tof_bin++) tof_weights_temp[tof_bin] /= tof_norm_coef;
}
*/
// compute and write the final TOF bin projection coefficients for current voxels
ap_ProjectionLine->AddVoxelAllTOFBins(a_direction, finalIdx * limitY1 * limitZ1, w[ 0 ] * weight * limitY1 * limitZ1, tof_weights_temp, tof_bin_first_for_voxel, tof_bin_last_for_voxel);
ap_ProjectionLine->AddVoxelAllTOFBins(a_direction, ( finalIdx + m_nbVoxXY ) * limitY1 * limitZ2, w[ 1 ] * weight * limitY1 * limitZ2 , tof_weights_temp, tof_bin_first_for_voxel, tof_bin_last_for_voxel);
ap_ProjectionLine->AddVoxelAllTOFBins(a_direction, ( finalIdx + mp_nbVox[ 0 ] + m_nbVoxXY ) * limitY2 * limitZ2, w[ 2 ] * weight * limitY2 * limitZ2 , tof_weights_temp, tof_bin_first_for_voxel, tof_bin_last_for_voxel);
ap_ProjectionLine->AddVoxelAllTOFBins(a_direction, ( finalIdx + mp_nbVox[ 0 ] ) * limitY2 * limitZ1, w[ 3 ] * weight * limitY2 * limitZ1 , tof_weights_temp, tof_bin_first_for_voxel, tof_bin_last_for_voxel);
limitY1 = 1.0; limitY2 = 1.0;
limitZ1 = 1.0; limitZ2 = 1.0;
}
delete[] tof_weights_temp;
}
else
{
// Computing the parametric values
// For the X-axis
// We introduce 1 voxel size around Y-axis
if( r[ 0 ] != 0.0 )
{
HPFLTNB const alphaX_0 = ( -mp_halfFOV[ 0 ] - mp_sizeVox[ 0 ] * 0.5 - event1[ 0 ] )
/ r[ 0 ];
HPFLTNB const alphaX_1 = ( mp_halfFOV[ 0 ] + mp_sizeVox[ 0 ] * 0.5 - event1[ 0 ] )
/ r[ 0 ];
alphaMin = std::max( alphaMin, std::min( alphaX_0, alphaX_1 ) );
alphaMax = std::min( alphaMax, std::max( alphaX_0, alphaX_1 ) );
}
// For the Y-axis
// Make a shift on first and last plane (like an offset)
// We stay in the limit of the image space
HPFLTNB const alphaY_0 = ( -mp_halfFOV[ 1 ] - event1[ 1 ] ) / r[ 1 ];
HPFLTNB const alphaY_1 = ( mp_halfFOV[ 1 ] - event1[ 1 ] ) / r[ 1 ];
alphaMin = std::max( alphaMin, std::min( alphaY_0, alphaY_1 ) );
alphaMax = std::min( alphaMax, std::max( alphaY_0, alphaY_1 ) );
// For the Z-axis
// We introduce 1 voxel size around Z-axis
// Make a shift on first and last plane (like an offset)
if( r[ 2 ] != 0.0 )
{
HPFLTNB const alphaZ_0 = ( -mp_halfFOV[ 2 ] - mp_sizeVox[ 2 ] * 0.5 - event1[ 2 ] )
/ r[ 2 ];
HPFLTNB const alphaZ_1 = ( mp_halfFOV[ 2 ] + mp_sizeVox[ 2 ] * 0.5 - event1[ 2 ] )
/ r[ 2 ];
alphaMin = std::max( alphaMin, std::min( alphaZ_0, alphaZ_1 ) );
alphaMax = std::min( alphaMax, std::max( alphaZ_0, alphaZ_1 ) );
}
if( alphaMax <= alphaMin ) return 0;
if( r[ 0 ] == 0.0 ) if( event1[ 0 ] < -mp_halfFOV[ 0 ] || event1[ 0 ] > mp_halfFOV[ 0 ] ) return 0;
if( r[ 2 ] == 0.0 ) if( event1[ 2 ] < -mp_halfFOV[ 2 ] || event1[ 2 ] > mp_halfFOV[ 2 ] ) return 0;
// temporary storage for TOF bins Gaussian integrals over the current voxel, allocation after potential returns
HPFLTNB* tof_weights_temp = new HPFLTNB[tof_nb_bins];
for (INTNB tof_bin=0; tof_bin 0.0 )
{
jMin = ::ceil( mp_nbVox[ 1 ] - m_toleranceY - ( mp_halfFOV[ 1 ] - alphaMin * r[ 1 ] - event1[ 1 ] ) / mp_sizeVox[ 1 ] );
jMax = ::floor( m_toleranceY + ( event1[ 1 ] + alphaMax * r[ 1 ] - (-mp_halfFOV[ 1 ]) ) / mp_sizeVox[ 1 ] );
}
else if( r[ 1 ] < 0.0 )
{
jMin = ::ceil( mp_nbVox[ 1 ] - m_toleranceY - ( mp_halfFOV[ 1 ] - alphaMax * r[ 1 ] - event1[ 1 ] ) / mp_sizeVox[ 1 ] );
jMax = ::floor( m_toleranceY + ( event1[ 1 ] + alphaMin * r[ 1 ] - (-mp_halfFOV[ 1 ]) ) / mp_sizeVox[ 1 ] );
}
// Computing an offset to get the correct position in plane
HPFLTNB const offset = -mp_halfFOV[ 1 ] + ( mp_sizeVox[ 1 ] * 0.5 ) - event1[ 1 ];
// Loop over all the crossing planes
for( int j = jMin; j < jMax; ++j )
{
// Computing position on crossed plane in term of grid spacing
HPFLTNB const step = offset + j * mp_sizeVox[ 1 ];
pos[ 0 ] = event1[ 0 ] + step * ri[ 0 ];
pos[ 2 ] = event1[ 2 ] + step * ri[ 2 ];
// Find the index in the image
index[ 0 ] = ::floor( m_toleranceX + ( pos[ 0 ] - m_boundX ) / mp_sizeVox[ 0 ] );
index[ 1 ] = ::floor( m_toleranceZ + ( pos[ 2 ] - m_boundZ ) / mp_sizeVox[ 2 ] );
finalIdx = ( index[ 0 ] - 1 ) + j * mp_nbVox[ 0 ] + ( index[ 1 ] - 1 ) * m_nbVoxXY;
// check if the main voxel is relevant
if (m_hasMask && (index[ 0 ] > 0 && index[ 1 ] > 0 && index[ 0 ] <= mp_nbVox[ 0 ] && index[ 1 ] <= mp_nbVox[ 2 ]) && !mp_mask[finalIdx]) continue;
// Compute the first and the last relevant TOF bin for this voxel
if (!m_TOFWeightingFcnPrecomputedFlag && m_TOFBinProperProcessingFlag)
{
// taking the TOF bins reached by the truncated Gaussian centered at the voxel center projection
tof_bin_first_for_voxel = max(tof_bin_first , (INTNB)(floor( (step * factor_for_tof - tof_half_span - lor_length_half - m_TOFBinSizeInMm/2.) / m_TOFBinSizeInMm )));
tof_bin_last_for_voxel = min(tof_bin_last , (INTNB)(ceil( (step * factor_for_tof + tof_half_span - lor_length_half + m_TOFBinSizeInMm/2.) / m_TOFBinSizeInMm )));
}
else
{
// taking the TOF bins whose TOF uncertainty function (centered at the bin center) reaches the voxel center projection
tof_bin_first_for_voxel = max(tof_bin_first , (INTNB)(ceil( (step * factor_for_tof - tof_half_span - lor_length_half ) / m_TOFBinSizeInMm )));
tof_bin_last_for_voxel = min(tof_bin_last , (INTNB)(floor( (step * factor_for_tof + tof_half_span - lor_length_half) / m_TOFBinSizeInMm )));
}
lor_tof_center = lor_length_half + tof_bin_first_for_voxel * m_TOFBinSizeInMm;
// if min/max TOF bins for this voxel do not fall into the total available TOF bins range, skip
if(tof_bin_first_for_voxel>tof_bin_last || tof_bin_last_for_voxel=0 && temp= mp_nbVox[ 0 ] ) limitX2 = 0;
// Check Z bounds
if( index[ 1 ] <= 0 ) limitZ1 = 0;
if( index[ 1 ] >= mp_nbVox[ 2 ] ) limitZ2 = 0;
// Storing values and indices
/*
if (tof_norm_coef>0.)
{
// first normalize TOF bin weights so that they sum to 1
for (INTNB tof_bin=tof_bin_first_for_voxel; tof_bin<=tof_bin_last_for_voxel; tof_bin++) tof_weights_temp[tof_bin] /= tof_norm_coef;
}
*/
// compute and write the final TOF bin projection coefficients for current voxels
ap_ProjectionLine->AddVoxelAllTOFBins(a_direction, finalIdx * limitX1 * limitZ1, w[ 0 ] * weight * limitX1 * limitZ1, tof_weights_temp, tof_bin_first_for_voxel, tof_bin_last_for_voxel);
ap_ProjectionLine->AddVoxelAllTOFBins(a_direction, ( finalIdx + m_nbVoxXY ) * limitX1 * limitZ2, w[ 1 ] * weight * limitX1 * limitZ2, tof_weights_temp, tof_bin_first_for_voxel, tof_bin_last_for_voxel);
ap_ProjectionLine->AddVoxelAllTOFBins(a_direction, ( finalIdx + 1 + m_nbVoxXY ) * limitX2 * limitZ2, w[ 2 ] * weight * limitX2 * limitZ2, tof_weights_temp, tof_bin_first_for_voxel, tof_bin_last_for_voxel);
ap_ProjectionLine->AddVoxelAllTOFBins(a_direction, ( finalIdx + 1 ) * limitX2 * limitZ1, w[ 3 ] * weight * limitX2 * limitZ1, tof_weights_temp, tof_bin_first_for_voxel, tof_bin_last_for_voxel);
limitX1 = 1; limitX2 = 1;
limitZ1 = 1; limitZ2 = 1;
}
delete[] tof_weights_temp;
}
return 0;
}
// =====================================================================
// ---------------------------------------------------------------------
// ---------------------------------------------------------------------
// =====================================================================