/*
-----------------------------------------------------------------------
Copyright: 2010-2021, imec Vision Lab, University of Antwerp
2014-2021, CWI, Amsterdam
Contact: astra@astra-toolbox.com
Website: http://www.astra-toolbox.com/
This file is part of the ASTRA Toolbox.
The ASTRA Toolbox 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.
The ASTRA Toolbox 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 the ASTRA Toolbox. If not, see .
-----------------------------------------------------------------------
*/
template
void CFanFlatBeamStripKernelProjector2D::project(Policy& p)
{
projectBlock_internal(0, m_pProjectionGeometry->getProjectionAngleCount(),
0, m_pProjectionGeometry->getDetectorCount(), p);
}
template
void CFanFlatBeamStripKernelProjector2D::projectSingleProjection(int _iProjection, Policy& p)
{
projectBlock_internal(_iProjection, _iProjection + 1,
0, m_pProjectionGeometry->getDetectorCount(), p);
}
template
void CFanFlatBeamStripKernelProjector2D::projectSingleRay(int _iProjection, int _iDetector, Policy& p)
{
projectBlock_internal(_iProjection, _iProjection + 1,
_iDetector, _iDetector + 1, p);
}
//----------------------------------------------------------------------------------------
// PROJECT BLOCK
template
void CFanFlatBeamStripKernelProjector2D::projectBlock_internal(int _iProjFrom, int _iProjTo, int _iDetFrom, int _iDetTo, Policy& p)
{
ASTRA_ASSERT(m_bIsInitialized);
// Some variables
float32 theta;
int row, col;
int iAngle, iDetector;
float32 res;
int x1L, x1R;
float32 x2L, x2R;
int iVolumeIndex, iRayIndex;
CFanFlatProjectionGeometry2D* projgeom = static_cast(m_pProjectionGeometry);
// Other precalculations
float32 PW = m_pVolumeGeometry->getPixelLengthX();
float32 PH = m_pVolumeGeometry->getPixelLengthY();
float32 DW = m_pProjectionGeometry->getDetectorWidth();
float32 inv_PW = 1.0f / PW;
float32 inv_PH = 1.0f / PH;
// calculate alpha's
float32 alpha;
float32* cos_alpha = new float32[m_pProjectionGeometry->getDetectorCount() + 1];
float32* sin_alpha = new float32[m_pProjectionGeometry->getDetectorCount() + 1];
for (int i = 0; i < m_pProjectionGeometry->getDetectorCount() + 1; ++i) {
alpha = -atan((i - m_pProjectionGeometry->getDetectorCount()*0.5f) * DW / projgeom->getSourceDetectorDistance());
cos_alpha[i] = cos(alpha);
sin_alpha[i] = sin(alpha);
}
// loop angles
for (iAngle = _iProjFrom; iAngle < _iProjTo; ++iAngle) {
// get values
theta = m_pProjectionGeometry->getProjectionAngle(iAngle);
bool switch_t = true;
if (theta >= 7*PIdiv4) theta -= 2*PI;
if (theta >= 3*PIdiv4) {
theta -= PI;
switch_t = false;
}
// Precalculate sin, cos, 1/cos
float32 sin_theta = sin(theta);
float32 cos_theta = cos(theta);
// [-45?,45?] and [135?,225?]
if (theta < PIdiv4) {
// loop detectors
for (iDetector = _iDetFrom; iDetector < _iDetTo; ++iDetector) {
iRayIndex = iAngle * m_pProjectionGeometry->getDetectorCount() + iDetector;
// POLICY: RAY PRIOR
if (!p.rayPrior(iRayIndex)) continue;
float32 dist_srcDetPixSquared = projgeom->getSourceDetectorDistance() * projgeom->getSourceDetectorDistance() + (iDetector + 0.5f - m_pProjectionGeometry->getDetectorCount()*0.5f) * (iDetector + 0.5f - m_pProjectionGeometry->getDetectorCount()*0.5f) * DW * DW;
dist_srcDetPixSquared = dist_srcDetPixSquared * dist_srcDetPixSquared / (projgeom->getSourceDetectorDistance() * projgeom->getSourceDetectorDistance() * DW * DW);
//float32 InvRayWidthSquared = (projgeom->getSourceDetectorDistance() * projgeom->getSourceDetectorDistance()) / dist_srcDetPixSquared;
float32 sin_theta_left, cos_theta_left;
float32 sin_theta_right, cos_theta_right;
// get theta_l = alpha_left + theta and theta_r = alpha_right + theta
float32 t_l, t_r;
if (!switch_t) {
sin_theta_left = sin_theta * cos_alpha[iDetector+1] + cos_theta * sin_alpha[iDetector+1];
sin_theta_right = sin_theta * cos_alpha[iDetector] + cos_theta * sin_alpha[iDetector];
cos_theta_left = cos_theta * cos_alpha[iDetector+1] - sin_theta * sin_alpha[iDetector+1];
cos_theta_right = cos_theta * cos_alpha[iDetector] - sin_theta * sin_alpha[iDetector];
t_l = sin_alpha[iDetector+1] * projgeom->getOriginSourceDistance();
t_r = sin_alpha[iDetector] * projgeom->getOriginSourceDistance();
} else {
sin_theta_left = sin_theta * cos_alpha[iDetector] + cos_theta * sin_alpha[iDetector];
sin_theta_right = sin_theta * cos_alpha[iDetector+1] + cos_theta * sin_alpha[iDetector+1];
cos_theta_left = cos_theta * cos_alpha[iDetector] - sin_theta * sin_alpha[iDetector];
cos_theta_right = cos_theta * cos_alpha[iDetector+1] - sin_theta * sin_alpha[iDetector+1];
t_l = -sin_alpha[iDetector] * projgeom->getOriginSourceDistance();
t_r = -sin_alpha[iDetector+1] * projgeom->getOriginSourceDistance();
}
float32 inv_cos_theta_left = 1.0f / cos_theta_left;
float32 inv_cos_theta_right = 1.0f / cos_theta_right;
float32 updateX_left = sin_theta_left * inv_cos_theta_left; // tan(theta_left)
float32 updateX_right = sin_theta_right * inv_cos_theta_right; // tan(theta_right)
// Precalculate kernel limits
// BUG: If updateX_left > 1 (which can happen if theta_left >= pi/4 > theta), then T_l > U_l, and the expressions for res are no longer correct
float32 S_l = -0.5f * updateX_left;
if (S_l > 0) {S_l = -S_l;}
float32 T_l = -S_l;
float32 U_l = 1.0f + S_l;
float32 V_l = 1.0f - S_l;
float32 inv_4T_l = 0.25f / T_l;
float32 S_r = -0.5f * updateX_right;
if (S_r > 0) {S_r = -S_r;}
float32 T_r = -S_r;
float32 U_r = 1.0f + S_r;
float32 V_r = 1.0f - S_r;
float32 inv_4T_r = 0.25f / T_r;
// calculate strip extremes (volume coordinates)
float32 PL = (t_l - sin_theta_left * m_pVolumeGeometry->pixelRowToCenterY(0)) * inv_cos_theta_left;
float32 PR = (t_r - sin_theta_right * m_pVolumeGeometry->pixelRowToCenterY(0)) * inv_cos_theta_right;
float32 PLimitL = PL + S_l * PH;
float32 PLimitR = PR - S_r * PH;
// calculate strip extremes (pixel coordinates)
float32 XLimitL = (PLimitL - m_pVolumeGeometry->getWindowMinX()) * inv_PW;
float32 XLimitR = (PLimitR - m_pVolumeGeometry->getWindowMinX()) * inv_PW;
float32 xL = (PL - m_pVolumeGeometry->getWindowMinX()) * inv_PW;
float32 xR = (PR - m_pVolumeGeometry->getWindowMinX()) * inv_PW;
// for each row
for (row = 0; row < m_pVolumeGeometry->getGridRowCount(); ++row) {
// get strip extremes in column indices
x1L = int((XLimitL > 0.0f) ? XLimitL : XLimitL-1.0f);
x1R = int((XLimitR > 0.0f) ? XLimitR : XLimitR-1.0f);
// get coords w.r.t leftmost column hit by strip
x2L = xL - x1L;
x2R = xR - x1L;
// update strip extremes for the next row
XLimitL += updateX_left;
XLimitR += updateX_right;
xL += updateX_left;
xR += updateX_right;
float32 diffSrcYSquared;
if (switch_t)
diffSrcYSquared = m_pVolumeGeometry->pixelRowToCenterY(row) + cos_theta * projgeom->getOriginSourceDistance();
else
diffSrcYSquared = m_pVolumeGeometry->pixelRowToCenterY(row) - cos_theta * projgeom->getOriginSourceDistance();
diffSrcYSquared = diffSrcYSquared * diffSrcYSquared;
// for each affected col
for (col = x1L; col <= x1R; ++col) {
if (col < 0 || col >= m_pVolumeGeometry->getGridColCount()) { x2L -= 1.0f; x2R -= 1.0f; continue; }
iVolumeIndex = m_pVolumeGeometry->pixelRowColToIndex(row, col);
// POLICY: PIXEL PRIOR
if (!p.pixelPrior(iVolumeIndex)) { x2L -= 1.0f; x2R -= 1.0f; continue; }
// right
if (x2R >= V_r) res = 1.0f;
else if (x2R > U_r) res = x2R - (x2R-U_r)*(x2R-U_r)*inv_4T_r;
else if (x2R >= T_r) res = x2R;
else if (x2R > S_r) res = (x2R-S_r)*(x2R-S_r) * inv_4T_r;
else { x2L -= 1.0f; x2R -= 1.0f; p.pixelPosterior(iVolumeIndex); continue; }
// left
if (x2L <= S_l) {}
else if (x2L < T_l) res -= (x2L-S_l)*(x2L-S_l) * inv_4T_l;
else if (x2L <= U_l) res -= x2L;
else if (x2L < V_l) res -= x2L - (x2L-U_l)*(x2L-U_l)*inv_4T_l;
else { x2L -= 1.0f; x2R -= 1.0f; p.pixelPosterior(iVolumeIndex); continue; }
float32 diffSrcX;
if (switch_t)
diffSrcX = m_pVolumeGeometry->pixelColToCenterX(col) - sin_theta * projgeom->getOriginSourceDistance();
else
diffSrcX = m_pVolumeGeometry->pixelColToCenterX(col) + sin_theta * projgeom->getOriginSourceDistance();
float32 scale = sqrt(dist_srcDetPixSquared / (diffSrcYSquared + diffSrcX * diffSrcX));
// POLICY: ADD
p.addWeight(iRayIndex, iVolumeIndex, PW*PH * res * scale);
// POLICY: PIXEL POSTERIOR
p.pixelPosterior(iVolumeIndex);
x2L -= 1.0f;
x2R -= 1.0f;
} // end col loop
} // end row loop
// POLICY: RAY POSTERIOR
p.rayPosterior(iRayIndex);
} // end detector loop
// [45?,135?] and [225?,315?]
// horizontaly
} else {
// loop detectors
for (iDetector = _iDetFrom; iDetector < _iDetTo; ++iDetector) {
iRayIndex = iAngle * m_pProjectionGeometry->getDetectorCount() + iDetector;
// POLICY: RAY PRIOR
if (!p.rayPrior(iRayIndex)) continue;
float32 dist_srcDetPixSquared = projgeom->getSourceDetectorDistance() * projgeom->getSourceDetectorDistance() + (iDetector + 0.5f - m_pProjectionGeometry->getDetectorCount()*0.5f) * (iDetector + 0.5f - m_pProjectionGeometry->getDetectorCount()*0.5f) * DW * DW;
dist_srcDetPixSquared = dist_srcDetPixSquared * dist_srcDetPixSquared / (projgeom->getSourceDetectorDistance() * projgeom->getSourceDetectorDistance() * DW * DW);
// get theta_l = alpha_left + theta and theta_r = alpha_right + theta
float32 sin_theta_left, cos_theta_left;
float32 sin_theta_right, cos_theta_right;
float32 t_l, t_r;
if (!switch_t) {
sin_theta_left = sin_theta * cos_alpha[iDetector] + cos_theta * sin_alpha[iDetector];
sin_theta_right = sin_theta * cos_alpha[iDetector+1] + cos_theta * sin_alpha[iDetector+1];
cos_theta_left = cos_theta * cos_alpha[iDetector] - sin_theta * sin_alpha[iDetector];
cos_theta_right = cos_theta * cos_alpha[iDetector+1] - sin_theta * sin_alpha[iDetector+1];
t_l = sin_alpha[iDetector] * projgeom->getOriginSourceDistance();
t_r = sin_alpha[iDetector+1] * projgeom->getOriginSourceDistance();
} else {
sin_theta_left = sin_theta * cos_alpha[iDetector+1] + cos_theta * sin_alpha[iDetector+1];
sin_theta_right = sin_theta * cos_alpha[iDetector] + cos_theta * sin_alpha[iDetector];
cos_theta_left = cos_theta * cos_alpha[iDetector+1] - sin_theta * sin_alpha[iDetector+1];
cos_theta_right = cos_theta * cos_alpha[iDetector] - sin_theta * sin_alpha[iDetector];
t_l = -sin_alpha[iDetector+1] * projgeom->getOriginSourceDistance();
t_r = -sin_alpha[iDetector] * projgeom->getOriginSourceDistance();
}
float32 inv_sin_theta_left = 1.0f / sin_theta_left;
float32 inv_sin_theta_right = 1.0f / sin_theta_right;
float32 updateX_left = cos_theta_left * inv_sin_theta_left;
float32 updateX_right = cos_theta_right * inv_sin_theta_right;
// Precalculate kernel limits
// BUG: If updateX_left > 1 (which can happen if theta_left < pi/4 <= theta), then T_l > U_l, and the expressions for res are no longer correct
float32 S_l = -0.5f * updateX_left;
if (S_l > 0) { S_l = -S_l; }
float32 T_l = -S_l;
float32 U_l = 1.0f + S_l;
float32 V_l = 1.0f - S_l;
float32 inv_4T_l = 0.25f / T_l;
float32 S_r = -0.5f * updateX_right;
if (S_r > 0) { S_r = -S_r; }
float32 T_r = -S_r;
float32 U_r = 1.0f + S_r;
float32 V_r = 1.0f - S_r;
float32 inv_4T_r = 0.25f / T_r;
// calculate strip extremes (volume coordinates)
float32 PL = (t_l - cos_theta_left * m_pVolumeGeometry->pixelColToCenterX(0)) * inv_sin_theta_left;
float32 PR = (t_r - cos_theta_right * m_pVolumeGeometry->pixelColToCenterX(0)) * inv_sin_theta_right;
float32 PLimitL = PL - S_l * PW;
float32 PLimitR = PR + S_r * PW;
// calculate strip extremes (pixel coordinates)
float32 XLimitL = (m_pVolumeGeometry->getWindowMaxY() - PLimitL) * inv_PH;
float32 XLimitR = (m_pVolumeGeometry->getWindowMaxY() - PLimitR) * inv_PH;
float32 xL = (m_pVolumeGeometry->getWindowMaxY() - PL) * inv_PH;
float32 xR = (m_pVolumeGeometry->getWindowMaxY() - PR) * inv_PH;
// for each col
for (col = 0; col < m_pVolumeGeometry->getGridColCount(); ++col) {
// get strip extremes in column indices
x1L = int((XLimitL > 0.0f) ? XLimitL : XLimitL-1.0f);
x1R = int((XLimitR > 0.0f) ? XLimitR : XLimitR-1.0f);
// get coords w.r.t leftmost column hit by strip
x2L = xL - x1L;
x2R = xR - x1L;
// update strip extremes for the next row
XLimitL += updateX_left;
XLimitR += updateX_right;
xL += updateX_left;
xR += updateX_right;
float32 diffSrcXSquared;
if (switch_t)
diffSrcXSquared = m_pVolumeGeometry->pixelColToCenterX(col) - sin_theta * projgeom->getOriginSourceDistance();
else
diffSrcXSquared = m_pVolumeGeometry->pixelColToCenterX(col) + sin_theta * projgeom->getOriginSourceDistance();
diffSrcXSquared = diffSrcXSquared * diffSrcXSquared;
// for each affected row
for (row = x1L; row <= x1R; ++row) {
if (row < 0 || row >= m_pVolumeGeometry->getGridRowCount()) { x2L -= 1.0f; x2R -= 1.0f; continue; }
iVolumeIndex = m_pVolumeGeometry->pixelRowColToIndex(row, col);
// POLICY: PIXEL PRIOR
if (!p.pixelPrior(iVolumeIndex)) { x2L -= 1.0f; x2R -= 1.0f; continue; }
// right
if (x2R >= V_r) res = 1.0f;
else if (x2R > U_r) res = x2R - (x2R-U_r)*(x2R-U_r)*inv_4T_r;
else if (x2R >= T_r) res = x2R;
else if (x2R > S_r) res = (x2R-S_r)*(x2R-S_r) * inv_4T_r;
else { x2L -= 1.0f; x2R -= 1.0f; p.pixelPosterior(iVolumeIndex); continue; }
// left
if (x2L <= S_l) {}
else if (x2L < T_l) res -= (x2L-S_l)*(x2L-S_l) * inv_4T_l;
else if (x2L <= U_l) res -= x2L;
else if (x2L < V_l) res -= x2L - (x2L-U_l)*(x2L-U_l)*inv_4T_l;
else { x2L -= 1.0f; x2R -= 1.0f; p.pixelPosterior(iVolumeIndex); continue; }
float32 diffSrcY;
if (switch_t)
diffSrcY = m_pVolumeGeometry->pixelRowToCenterY(row) + cos_theta * projgeom->getOriginSourceDistance();
else
diffSrcY = m_pVolumeGeometry->pixelRowToCenterY(row) - cos_theta * projgeom->getOriginSourceDistance();
float32 scale = sqrt(dist_srcDetPixSquared / (diffSrcXSquared + diffSrcY * diffSrcY));
// POLICY: ADD
p.addWeight(iRayIndex, iVolumeIndex, PW*PH * res * scale);
// POLICY: PIXEL POSTERIOR
p.pixelPosterior(iVolumeIndex);
x2L -= 1.0f;
x2R -= 1.0f;
} // end col loop
} // end row loop
// POLICY: RAY POSTERIOR
p.rayPosterior(iRayIndex);
} // end detector loop
} // end theta switch
} // end angle loop
delete[] cos_alpha;
delete[] sin_alpha;
}