summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--astra_vc11.vcxproj2
-rw-r--r--astra_vc11.vcxproj.filters9
-rw-r--r--build/linux/Makefile.in1
-rw-r--r--cuda/2d/astra.cu11
-rw-r--r--include/astra/FanFlatBeamLineKernelProjector2D.inl305
-rw-r--r--include/astra/FanFlatProjectionGeometry2D.h5
-rw-r--r--include/astra/GeometryUtil2D.h12
-rw-r--r--include/astra/ParallelBeamBlobKernelProjector2D.h7
-rw-r--r--include/astra/ParallelBeamBlobKernelProjector2D.inl312
-rw-r--r--include/astra/ParallelBeamLineKernelProjector2D.h2
-rw-r--r--include/astra/ParallelBeamLineKernelProjector2D.inl400
-rw-r--r--include/astra/ParallelBeamLinearKernelProjector2D.h2
-rw-r--r--include/astra/ParallelBeamLinearKernelProjector2D.inl257
-rw-r--r--include/astra/ParallelBeamStripKernelProjector2D.h1
-rw-r--r--include/astra/ParallelBeamStripKernelProjector2D.inl464
-rw-r--r--include/astra/ParallelProjectionGeometry2D.h10
-rw-r--r--include/astra/ParallelVecProjectionGeometry2D.h163
-rw-r--r--include/astra/ProjectionGeometry2D.h13
-rw-r--r--matlab/algorithms/DART/IterativeTomography.m2
-rw-r--r--matlab/algorithms/DART/examples/example1.m53
-rw-r--r--matlab/mex/astra_mex_data2d_c.cpp5
-rw-r--r--matlab/tools/astra_create_proj_geom.m13
-rw-r--r--matlab/tools/astra_geom_2vec.m76
-rw-r--r--matlab/tools/astra_geom_postalignment.m33
-rw-r--r--matlab/tools/astra_geom_size.m36
-rw-r--r--matlab/tools/astra_geom_visualize.m216
-rw-r--r--python/astra/PyIncludes.pxd4
-rw-r--r--python/astra/creators.py13
-rw-r--r--python/astra/data2d_c.pyx4
-rw-r--r--python/astra/functions.py56
-rw-r--r--src/FanFlatProjectionGeometry2D.cpp18
-rw-r--r--src/ParallelBeamBlobKernelProjector2D.cpp93
-rw-r--r--src/ParallelBeamLineKernelProjector2D.cpp108
-rw-r--r--src/ParallelBeamLinearKernelProjector2D.cpp2
-rw-r--r--src/ParallelBeamStripKernelProjector2D.cpp100
-rw-r--r--src/ParallelProjectionGeometry2D.cpp25
-rw-r--r--src/ParallelVecProjectionGeometry2D.cpp233
-rw-r--r--src/ProjectionGeometry2D.cpp18
-rw-r--r--src/Projector2D.cpp5
39 files changed, 1968 insertions, 1121 deletions
diff --git a/astra_vc11.vcxproj b/astra_vc11.vcxproj
index d050b96..cd73076 100644
--- a/astra_vc11.vcxproj
+++ b/astra_vc11.vcxproj
@@ -538,6 +538,7 @@
<ClCompile Include="src\ParallelBeamStripKernelProjector2D.cpp" />
<ClCompile Include="src\ParallelProjectionGeometry2D.cpp" />
<ClCompile Include="src\ParallelProjectionGeometry3D.cpp" />
+ <ClCompile Include="src\ParallelVecProjectionGeometry2D.cpp" />
<ClCompile Include="src\ParallelVecProjectionGeometry3D.cpp" />
<ClCompile Include="src\PlatformDepSystemCode.cpp" />
<ClCompile Include="src\PluginAlgorithm.cpp" />
@@ -652,6 +653,7 @@
<ClInclude Include="include\astra\ParallelProjectionGeometry2D.h" />
<ClInclude Include="include\astra\ParallelProjectionGeometry3D.h" />
<ClInclude Include="include\astra\ParallelVecProjectionGeometry3D.h" />
+ <ClInclude Include="include\astra\ParallelVecProjectionGeometry2D.h" />
<ClInclude Include="include\astra\PlatformDepSystemCode.h" />
<ClInclude Include="include\astra\PluginAlgorithm.h" />
<ClInclude Include="include\astra\ProjectionGeometry2D.h" />
diff --git a/astra_vc11.vcxproj.filters b/astra_vc11.vcxproj.filters
index dd7f574..4143cd9 100644
--- a/astra_vc11.vcxproj.filters
+++ b/astra_vc11.vcxproj.filters
@@ -321,6 +321,10 @@
<ClCompile Include="src\CudaSirtAlgorithm3D.cpp">
<Filter>CUDA\astra source</Filter>
</ClCompile>
+ <ClCompile Include="src\GeometryUtil3D.cpp" />
+ <ClCompile Include="src\ParallelVecProjectionGeometry2D.cpp">
+ <Filter>Geometries\source</Filter>
+ </ClCompile>
</ItemGroup>
<ItemGroup>
<ClInclude Include="include\astra\Algorithm.h">
@@ -671,6 +675,11 @@
<ClInclude Include="cuda\3d\util3d.h">
<Filter>CUDA\cuda headers</Filter>
</ClInclude>
+ <ClInclude Include="include\astra\GeometryUtil2D.h" />
+ <ClInclude Include="include\astra\GeometryUtil3D.h" />
+ <ClInclude Include="include\astra\ParallelVecProjectionGeometry2D.h">
+ <Filter>Geometries\headers</Filter>
+ </ClInclude>
</ItemGroup>
<ItemGroup>
<None Include="include\astra\DataProjectorPolicies.inl">
diff --git a/build/linux/Makefile.in b/build/linux/Makefile.in
index f10f482..c72e20f 100644
--- a/build/linux/Makefile.in
+++ b/build/linux/Makefile.in
@@ -147,6 +147,7 @@ BASE_OBJECTS=\
src/ParallelBeamLineKernelProjector2D.lo \
src/ParallelBeamStripKernelProjector2D.lo \
src/ParallelProjectionGeometry2D.lo \
+ src/ParallelVecProjectionGeometry2D.lo \
src/ParallelProjectionGeometry3D.lo \
src/ParallelVecProjectionGeometry3D.lo \
src/PlatformDepSystemCode.lo \
diff --git a/cuda/2d/astra.cu b/cuda/2d/astra.cu
index 4c69628..c1e6566 100644
--- a/cuda/2d/astra.cu
+++ b/cuda/2d/astra.cu
@@ -826,17 +826,12 @@ bool convertAstraGeometry(const CVolumeGeometry2D* pVolGeom,
// If there are existing detector offsets, or if we need to translate,
// we need to return offsets
- if (pProjGeom->getExtraDetectorOffset() || offCenter)
+ if (offCenter)
{
float* offset = new float[nth];
- if (pProjGeom->getExtraDetectorOffset()) {
- for (int i = 0; i < nth; ++i)
- offset[i] = pProjGeom->getExtraDetectorOffset()[i];
- } else {
- for (int i = 0; i < nth; ++i)
- offset[i] = 0.0f;
- }
+ for (int i = 0; i < nth; ++i)
+ offset[i] = 0.0f;
if (offCenter) {
float dx = (pVolGeom->getWindowMinX() + pVolGeom->getWindowMaxX()) / 2;
diff --git a/include/astra/FanFlatBeamLineKernelProjector2D.inl b/include/astra/FanFlatBeamLineKernelProjector2D.inl
index 2f87659..23438fb 100644
--- a/include/astra/FanFlatBeamLineKernelProjector2D.inl
+++ b/include/astra/FanFlatBeamLineKernelProjector2D.inl
@@ -25,9 +25,7 @@ along with the ASTRA Toolbox. If not, see <http://www.gnu.org/licenses/>.
-----------------------------------------------------------------------
$Id$
*/
-
-
-using namespace astra;
+#define policy_weight(p,rayindex,volindex,weight) if (p.pixelPrior(volindex)) { p.addWeight(rayindex, volindex, weight); p.pixelPosterior(volindex); }
template <typename Policy>
void CFanFlatBeamLineKernelProjector2D::project(Policy& p)
@@ -51,246 +49,165 @@ void CFanFlatBeamLineKernelProjector2D::projectSingleRay(int _iProjection, int _
}
//----------------------------------------------------------------------------------------
-// PROJECT BLOCK
+// PROJECT BLOCK - vector projection geometry
template <typename Policy>
void CFanFlatBeamLineKernelProjector2D::projectBlock_internal(int _iProjFrom, int _iProjTo, int _iDetFrom, int _iDetTo, Policy& p)
{
- // variables
- float32 sin_theta, cos_theta, inv_sin_theta, inv_cos_theta, S, T, t, I, P, x, x2;
- float32 lengthPerRow, updatePerRow, inv_pixelLengthX, lengthPerCol, updatePerCol, inv_pixelLengthY;
- int iVolumeIndex, iRayIndex, row, col, iAngle, iDetector, x1;
- bool switch_t;
+ // get vector geometry
+ const CFanFlatVecProjectionGeometry2D* pVecProjectionGeometry;
+ if (dynamic_cast<CFanFlatProjectionGeometry2D*>(m_pProjectionGeometry)) {
+ pVecProjectionGeometry = dynamic_cast<CFanFlatProjectionGeometry2D*>(m_pProjectionGeometry)->toVectorGeometry();
+ } else {
+ pVecProjectionGeometry = dynamic_cast<CFanFlatVecProjectionGeometry2D*>(m_pProjectionGeometry);
+ }
+
+ // precomputations
+ const float32 pixelLengthX = m_pVolumeGeometry->getPixelLengthX();
+ const float32 pixelLengthY = m_pVolumeGeometry->getPixelLengthY();
+ const float32 inv_pixelLengthX = 1.0f / pixelLengthX;
+ const float32 inv_pixelLengthY = 1.0f / pixelLengthY;
+ const int colCount = m_pVolumeGeometry->getGridColCount();
+ const int rowCount = m_pVolumeGeometry->getGridRowCount();
+ const int detCount = pVecProjectionGeometry->getDetectorCount();
+ const float32 Ex = m_pVolumeGeometry->getWindowMinY() + pixelLengthX*0.5f;
+ const float32 Ey = m_pVolumeGeometry->getWindowMaxY() - pixelLengthY*0.5f;
- const CFanFlatProjectionGeometry2D* pProjectionGeometry = dynamic_cast<CFanFlatProjectionGeometry2D*>(m_pProjectionGeometry);
- const CFanFlatVecProjectionGeometry2D* pVecProjectionGeometry = dynamic_cast<CFanFlatVecProjectionGeometry2D*>(m_pProjectionGeometry);
+ // loop angles
+ #pragma omp parallel for
+ for (int iAngle = _iProjFrom; iAngle < _iProjTo; ++iAngle) {
- float32 old_theta, theta, alpha;
- const SFanProjection * proj = 0;
+ // variables
+ float32 Dx, Dy, Rx, Ry, S, T, weight, c, r, deltac, deltar, offset, RxOverRy, RyOverRx;
+ float32 lengthPerRow, lengthPerCol, invTminSTimesLengthPerRow, invTminSTimesLengthPerCol;
+ int iVolumeIndex, iRayIndex, row, col, iDetector;
- // loop angles
- for (iAngle = _iProjFrom; iAngle < _iProjTo; ++iAngle) {
-
- // get theta
- if (pProjectionGeometry) {
- old_theta = pProjectionGeometry->getProjectionAngle(iAngle);
- }
- else if (pVecProjectionGeometry) {
- proj = &pVecProjectionGeometry->getProjectionVectors()[iAngle];
- old_theta = atan2(-proj->fSrcX, proj->fSrcY);
- if (old_theta < 0) old_theta += 2*PI;
- } else {
- assert(false);
- }
-
- switch_t = false;
- if (old_theta >= 7*PIdiv4) old_theta -= 2*PI;
- if (old_theta >= 3*PIdiv4) {
- old_theta -= PI;
- switch_t = true;
- }
+ const SFanProjection * proj = &pVecProjectionGeometry->getProjectionVectors()[iAngle];
// loop detectors
for (iDetector = _iDetFrom; iDetector < _iDetTo; ++iDetector) {
- iRayIndex = iAngle * m_pProjectionGeometry->getDetectorCount() + iDetector;
+ iRayIndex = iAngle * detCount + iDetector;
// POLICY: RAY PRIOR
if (!p.rayPrior(iRayIndex)) continue;
-
- // get values
- if (pProjectionGeometry) {
- t = -pProjectionGeometry->indexToDetectorOffset(iDetector);
- alpha = atan(t / pProjectionGeometry->getSourceDetectorDistance());
- t = sin(alpha) * pProjectionGeometry->getOriginSourceDistance();
- }
- else if (pVecProjectionGeometry) {
- float32 detX = proj->fDetSX + proj->fDetUX*(0.5f + iDetector);
- float32 detY = proj->fDetSY + proj->fDetUY*(0.5f + iDetector);
- alpha = angleBetweenVectors(-proj->fSrcX, -proj->fSrcY, detX - proj->fSrcX, detY - proj->fSrcY);
- t = sin(alpha) * sqrt(proj->fSrcX*proj->fSrcX + proj->fSrcY*proj->fSrcY);
+
+ Dx = proj->fDetSX + (iDetector+0.5f) * proj->fDetUX;
+ Dy = proj->fDetSY + (iDetector+0.5f) * proj->fDetUY;
+
+ Rx = proj->fSrcX - Dx;
+ Ry = proj->fSrcY - Dy;
+
+ bool vertical = fabs(Rx) < fabs(Ry);
+ if (vertical) {
+ RxOverRy = Rx/Ry;
+ lengthPerRow = pixelLengthX * sqrt(Rx*Rx + Ry*Ry) / abs(Ry);
+ deltac = -pixelLengthY * RxOverRy * inv_pixelLengthX;
+ S = 0.5f - 0.5f*fabs(RxOverRy);
+ T = 0.5f + 0.5f*fabs(RxOverRy);
+ invTminSTimesLengthPerRow = lengthPerRow / (T - S);
} else {
- assert(false);
+ RyOverRx = Ry/Rx;
+ lengthPerCol = pixelLengthY * sqrt(Rx*Rx + Ry*Ry) / abs(Rx);
+ deltar = -pixelLengthX * RyOverRx * inv_pixelLengthY;
+ S = 0.5f - 0.5f*fabs(RyOverRx);
+ T = 0.5f + 0.5f*fabs(RyOverRx);
+ invTminSTimesLengthPerCol = lengthPerCol / (T - S);
}
- if (switch_t) t = -t;
- theta = old_theta + alpha;
-
- // precalculate sin, cos, 1/cos
- sin_theta = sin(theta);
- cos_theta = cos(theta);
- inv_sin_theta = 1.0f / sin_theta;
- inv_cos_theta = 1.0f / cos_theta;
-
- // precalculate kernel limits
- lengthPerRow = m_pVolumeGeometry->getPixelLengthY() * inv_cos_theta;
- updatePerRow = sin_theta * inv_cos_theta;
- inv_pixelLengthX = 1.0f / m_pVolumeGeometry->getPixelLengthX();
-
- // precalculate kernel limits
- lengthPerCol = m_pVolumeGeometry->getPixelLengthX() * inv_sin_theta;
- updatePerCol = cos_theta * inv_sin_theta;
- inv_pixelLengthY = 1.0f / m_pVolumeGeometry->getPixelLengthY();
-
- // precalculate S and T
- S = 0.5f - 0.5f * ((updatePerRow < 0) ? -updatePerRow : updatePerRow);
- T = 0.5f - 0.5f * ((updatePerCol < 0) ? -updatePerCol : updatePerCol);
+ bool isin = false;
// vertically
- if (old_theta <= PIdiv4) {
-
- // calculate x for row 0
- P = (t - sin_theta * m_pVolumeGeometry->pixelRowToCenterY(0)) * inv_cos_theta;
- x = (P - m_pVolumeGeometry->getWindowMinX()) * inv_pixelLengthX;
+ if (vertical) {
+
+ // calculate c for row 0
+ c = (Dx + (Ey - Dy)*RxOverRy - Ex) * inv_pixelLengthX;
// for each row
- for (row = 0; row < m_pVolumeGeometry->getGridRowCount(); ++row) {
-
- // get coords
- x1 = int((x > 0.0f) ? x : x-1.0f);
- x2 = x - x1;
- x += updatePerRow;
+ for (row = 0; row < rowCount; ++row, c += deltac) {
- if (x1 < -1 || x1 > m_pVolumeGeometry->getGridColCount()) continue;
+ col = int(c+0.5f);
+ if (col <= 0 || col >= colCount-1) { if (!isin) continue; else break; }
+ offset = c - float32(col);
// left
- if (x2 < 0.5f-S) {
- I = (0.5f - S + x2) / (1.0f - 2.0f*S) * lengthPerRow;
-
- if (x1-1 >= 0 /*&& x1-1 < m_pVolumeGeometry->getGridColCount()*/) {//x1 is always less than or equal to gridColCount because of the "continue" in the beginning of the for-loop
- iVolumeIndex = m_pVolumeGeometry->pixelRowColToIndex(row, x1-1);
- // POLICY: PIXEL PRIOR + ADD + POSTERIOR
- if (p.pixelPrior(iVolumeIndex)) {
- p.addWeight(iRayIndex, iVolumeIndex, lengthPerRow-I);
- p.pixelPosterior(iVolumeIndex);
- }
- }
-
- if (x1 >= 0 && x1 < m_pVolumeGeometry->getGridColCount()) {
- iVolumeIndex = m_pVolumeGeometry->pixelRowColToIndex(row, x1);
- // POLICY: PIXEL PRIOR + ADD + POSTERIOR
- if (p.pixelPrior(iVolumeIndex)) {
- p.addWeight(iRayIndex, iVolumeIndex, I);
- p.pixelPosterior(iVolumeIndex);
- }
- }
- }
+ if (offset < -S) {
+ weight = (offset + T) * invTminSTimesLengthPerRow;
- // center
- else if (x2 <= 0.5f+S) {
- if (x1 >= 0 && x1 < m_pVolumeGeometry->getGridColCount()) {
- iVolumeIndex = m_pVolumeGeometry->pixelRowColToIndex(row, x1);
- // POLICY: PIXEL PRIOR + ADD + POSTERIOR
- if (p.pixelPrior(iVolumeIndex)) {
- p.addWeight(iRayIndex, iVolumeIndex, lengthPerRow);
- p.pixelPosterior(iVolumeIndex);
- }
- }
+ iVolumeIndex = row * colCount + col - 1;
+ policy_weight(p, iRayIndex, iVolumeIndex, lengthPerRow-weight)
+
+ iVolumeIndex++;
+ policy_weight(p, iRayIndex, iVolumeIndex, weight)
}
// right
- else if (x2 <= 1.0f) {
- I = (1.5f - S - x2) / (1.0f - 2.0f*S) * lengthPerRow;
-
- if (x1 >= 0 && x1 < m_pVolumeGeometry->getGridColCount()) {
- iVolumeIndex = m_pVolumeGeometry->pixelRowColToIndex(row, x1);
- // POLICY: PIXEL PRIOR + ADD + POSTERIOR
- if (p.pixelPrior(iVolumeIndex)) {
- p.addWeight(iRayIndex, iVolumeIndex, I);
- p.pixelPosterior(iVolumeIndex);
- }
- }
- if (/*x1+1 >= 0 &&*/ x1+1 < m_pVolumeGeometry->getGridColCount()) {//x1 is always greater than or equal to -1 because of the "continue" in the beginning of the for-loop
- iVolumeIndex = m_pVolumeGeometry->pixelRowColToIndex(row, x1+1);
- // POLICY: PIXEL PRIOR + ADD + POSTERIOR
- if (p.pixelPrior(iVolumeIndex)) {
- p.addWeight(iRayIndex, iVolumeIndex, lengthPerRow-I);
- p.pixelPosterior(iVolumeIndex);
- }
- }
+ else if (S < offset) {
+ weight = (offset - S) * invTminSTimesLengthPerRow;
+
+ iVolumeIndex = row * colCount + col;
+ policy_weight(p, iRayIndex, iVolumeIndex, lengthPerRow-weight)
+
+ iVolumeIndex++;
+ policy_weight(p, iRayIndex, iVolumeIndex, weight)
}
+
+ // centre
+ else {
+ iVolumeIndex = row * colCount + col;
+ policy_weight(p, iRayIndex, iVolumeIndex, lengthPerRow)
+ }
+ isin = true;
}
}
// horizontally
- //else if (PIdiv4 <= old_theta && old_theta <= 3*PIdiv4) {
else {
- // calculate point P
- P = (t - cos_theta * m_pVolumeGeometry->pixelColToCenterX(0)) * inv_sin_theta;
- x = (m_pVolumeGeometry->getWindowMaxY() - P) * inv_pixelLengthY;
+ // calculate r for col 0
+ r = -(Dy + (Ex - Dx)*RyOverRx - Ey) * inv_pixelLengthY;
// for each col
- for (col = 0; col < m_pVolumeGeometry->getGridColCount(); ++col) {
-
- // get coords
- x1 = int((x > 0.0f) ? x : x-1.0f);
- x2 = x - x1;
- x += updatePerCol;
+ for (col = 0; col < colCount; ++col, r += deltar) {
- if (x1 < -1 || x1 > m_pVolumeGeometry->getGridRowCount()) continue;
+ int row = int(r+0.5f);
+ if (row <= 0 || row >= rowCount-1) { if (!isin) continue; else break; }
+ offset = r - float32(row);
// up
- if (x2 < 0.5f-T) {
- I = (0.5f - T + x2) / (1.0f - 2.0f*T) * lengthPerCol;
-
- if (x1-1 >= 0 /*&& x1-1 < m_pVolumeGeometry->getGridRowCount()*/) {//x1 is always less than or equal to gridRowCount because of the "continue" in the beginning of the for-loop
- iVolumeIndex = m_pVolumeGeometry->pixelRowColToIndex(x1-1, col);
- // POLICY: PIXEL PRIOR + ADD + POSTERIOR
- if (p.pixelPrior(iVolumeIndex)) {
- p.addWeight(iRayIndex, iVolumeIndex, lengthPerCol-I);
- p.pixelPosterior(iVolumeIndex);
- }
- }
-
- if (x1 >= 0 && x1 < m_pVolumeGeometry->getGridRowCount()) {
- iVolumeIndex = m_pVolumeGeometry->pixelRowColToIndex(x1, col);
- // POLICY: PIXEL PRIOR + ADD + POSTERIOR
- if (p.pixelPrior(iVolumeIndex)) {
- p.addWeight(iRayIndex, iVolumeIndex, I);
- p.pixelPosterior(iVolumeIndex);
- }
- }
- }
+ if (offset < -S) {
+ weight = (offset + T) * invTminSTimesLengthPerCol;
+
+ iVolumeIndex = (row-1) * colCount + col;
+ policy_weight(p, iRayIndex, iVolumeIndex, lengthPerCol-weight)
- // center
- else if (x2 <= 0.5f+T) {
- if (x1 >= 0 && x1 < m_pVolumeGeometry->getGridRowCount()) {
- iVolumeIndex = m_pVolumeGeometry->pixelRowColToIndex(x1, col);
- // POLICY: PIXEL PRIOR + ADD + POSTERIOR
- if (p.pixelPrior(iVolumeIndex)) {
- p.addWeight(iRayIndex, iVolumeIndex, lengthPerCol);
- p.pixelPosterior(iVolumeIndex);
- }
- }
+ iVolumeIndex += colCount;
+ policy_weight(p, iRayIndex, iVolumeIndex, weight)
}
// down
- else if (x2 <= 1.0f) {
- I = (1.5f - T - x2) / (1.0f - 2.0f*T) * lengthPerCol;
-
- if (x1 >= 0 && x1 < m_pVolumeGeometry->getGridRowCount()) {
- iVolumeIndex = m_pVolumeGeometry->pixelRowColToIndex(x1, col);
- // POLICY: PIXEL PRIOR + ADD + POSTERIOR
- if (p.pixelPrior(iVolumeIndex)) {
- p.addWeight(iRayIndex, iVolumeIndex, I);
- p.pixelPosterior(iVolumeIndex);
- }
- }
- if (/*x1+1 >= 0 &&*/ x1+1 < m_pVolumeGeometry->getGridRowCount()) {//x1 is always greater than or equal to -1 because of the "continue" in the beginning of the for-loop
- iVolumeIndex = m_pVolumeGeometry->pixelRowColToIndex(x1+1, col);
- // POLICY: PIXEL PRIOR + ADD + POSTERIOR
- if (p.pixelPrior(iVolumeIndex)) {
- p.addWeight(iRayIndex, iVolumeIndex, lengthPerCol-I);
- p.pixelPosterior(iVolumeIndex);
- }
- }
+ else if (S < offset) {
+ weight = (offset - S) * invTminSTimesLengthPerCol;
+
+ iVolumeIndex = row * colCount + col;
+ policy_weight(p, iRayIndex, iVolumeIndex, lengthPerCol-weight)
+
+ iVolumeIndex += colCount;
+ policy_weight(p, iRayIndex, iVolumeIndex, weight)
+ }
+
+ // centre
+ else {
+ iVolumeIndex = row * colCount + col;
+ policy_weight(p, iRayIndex, iVolumeIndex, lengthPerCol)
}
+ isin = true;
}
- } // end loop col
+ }
// POLICY: RAY POSTERIOR
p.rayPosterior(iRayIndex);
} // end loop detector
} // end loop angles
+
}
diff --git a/include/astra/FanFlatProjectionGeometry2D.h b/include/astra/FanFlatProjectionGeometry2D.h
index 180fe68..8750232 100644
--- a/include/astra/FanFlatProjectionGeometry2D.h
+++ b/include/astra/FanFlatProjectionGeometry2D.h
@@ -30,6 +30,7 @@ $Id$
#define _INC_ASTRA_FANFLATPROJECTIONGEOMETRY2D
#include "ProjectionGeometry2D.h"
+#include "FanFlatVecProjectionGeometry2D.h"
#include <cmath>
@@ -190,6 +191,10 @@ public:
* @return a unit vector describing the direction
*/
virtual CVector3D getProjectionDirection(int _iProjectionIndex, int _iDetectorIndex);
+
+ /** Create a vector geom
+ */
+ CFanFlatVecProjectionGeometry2D* toVectorGeometry();
};
diff --git a/include/astra/GeometryUtil2D.h b/include/astra/GeometryUtil2D.h
index d4ee92e..680cecd 100644
--- a/include/astra/GeometryUtil2D.h
+++ b/include/astra/GeometryUtil2D.h
@@ -31,6 +31,18 @@ $Id$
namespace astra {
+struct SParProjection {
+ // the ray direction
+ float fRayX, fRayY;
+
+ // the start of the (linear) detector
+ float fDetSX, fDetSY;
+
+ // the length of a single detector pixel
+ float fDetUX, fDetUY;
+};
+
+
struct SFanProjection {
// the source
float fSrcX, fSrcY;
diff --git a/include/astra/ParallelBeamBlobKernelProjector2D.h b/include/astra/ParallelBeamBlobKernelProjector2D.h
index ecf71ef..a718f56 100644
--- a/include/astra/ParallelBeamBlobKernelProjector2D.h
+++ b/include/astra/ParallelBeamBlobKernelProjector2D.h
@@ -215,7 +215,12 @@ protected:
float32 m_fBlobSampleRate; //< At which interval are the inserted blob values evaluated?
int m_iBlobSampleCount; //< Number of evaluated blob samples
float32* m_pfBlobValues; //< Evaluated blob values
- float32* m_pfBlobValuesNeg; //< Evaluated blob values
+
+ /** Internal policy-based projection of a range of angles and range.
+ * (_i*From is inclusive, _i*To exclusive) */
+ template <typename Policy>
+ void projectBlock_internal(int _iProjFrom, int _iProjTo,
+ int _iDetFrom, int _iDetTo, Policy& _policy);
};
diff --git a/include/astra/ParallelBeamBlobKernelProjector2D.inl b/include/astra/ParallelBeamBlobKernelProjector2D.inl
index 467e066..c2aa193 100644
--- a/include/astra/ParallelBeamBlobKernelProjector2D.inl
+++ b/include/astra/ParallelBeamBlobKernelProjector2D.inl
@@ -27,186 +27,206 @@ $Id$
*/
-
-//----------------------------------------------------------------------------------------
-// PROJECT ALL
template <typename Policy>
void CParallelBeamBlobKernelProjector2D::project(Policy& p)
{
- for (int iAngle = 0; iAngle < m_pProjectionGeometry->getProjectionAngleCount(); ++iAngle) {
- for (int iDetector = 0; iDetector < m_pProjectionGeometry->getDetectorCount(); ++iDetector) {
- projectSingleRay(iAngle, iDetector, p);
- }
- }
+ projectBlock_internal(0, m_pProjectionGeometry->getProjectionAngleCount(),
+ 0, m_pProjectionGeometry->getDetectorCount(), p);
}
-
-//----------------------------------------------------------------------------------------
-// PROJECT SINGLE PROJECTION
template <typename Policy>
void CParallelBeamBlobKernelProjector2D::projectSingleProjection(int _iProjection, Policy& p)
{
- for (int iDetector = 0; iDetector < m_pProjectionGeometry->getDetectorCount(); ++iDetector) {
- projectSingleRay(_iProjection, iDetector, p);
- }
+ projectBlock_internal(_iProjection, _iProjection + 1,
+ 0, m_pProjectionGeometry->getDetectorCount(), p);
}
-
-
-//----------------------------------------------------------------------------------------
-// PROJECT SINGLE RAY
template <typename Policy>
void CParallelBeamBlobKernelProjector2D::projectSingleRay(int _iProjection, int _iDetector, Policy& p)
{
- ASTRA_ASSERT(m_bIsInitialized);
+ projectBlock_internal(_iProjection, _iProjection + 1,
+ _iDetector, _iDetector + 1, p);
+}
- int iRayIndex = _iProjection * m_pProjectionGeometry->getDetectorCount() + _iDetector;
+//----------------------------------------------------------------------------------------
+// PROJECT BLOCK - vector projection geometry
+//
+// Kernel limitations: isotropic pixels (PixelLengthX == PixelLengthY)
+//
+// For each angle/detector pair:
+//
+// Let D=(Dx,Dy) denote the centre of the detector (point) in volume coordinates, and
+// let R=(Rx,Ry) denote the direction of the ray (vector).
+//
+// For mainly vertical rays (|Rx|<=|Ry|),
+// let E=(Ex,Ey) denote the centre of the most upper left pixel:
+// E = (WindowMinX + PixelLengthX/2, WindowMaxY - PixelLengthY/2),
+// and let F=(Fx,Fy) denote a vector to the next pixel
+// F = (PixelLengthX, 0)
+//
+// The intersection of the ray (D+aR) with the centre line of the upper row of pixels (E+bF) is
+// { Dx + a*Rx = Ex + b*Fx
+// { Dy + a*Ry = Ey + b*Fy
+// Solving for (a,b) results in:
+// a = (Ey + b*Fy - Dy)/Ry
+// = (Ey - Dy)/Ry
+// b = (Dx + a*Rx - Ex)/Fx
+// = (Dx + (Ey - Dy)*Rx/Ry - Ex)/Fx
+//
+// Define c as the x-value of the intersection of the ray with the upper row in pixel coordinates.
+// c = b
+//
+// The intersection of the ray (D+aR) with the centre line of the second row of pixels (E'+bF) with
+// E'=(WindowMinX + PixelLengthX/2, WindowMaxY - 3*PixelLengthY/2)
+// expressed in x-value pixel coordinates is
+// c' = (Dx + (Ey' - Dy)*Rx/Ry - Ex)/Fx.
+// And thus:
+// deltac = c' - c = (Dx + (Ey' - Dy)*Rx/Ry - Ex)/Fx - (Dx + (Ey - Dy)*Rx/Ry - Ex)/Fx
+// = [(Ey' - Dy)*Rx/Ry - (Ey - Dy)*Rx/Ry]/Fx
+// = [Ey' - Ey]*(Rx/Ry)/Fx
+// = [Ey' - Ey]*(Rx/Ry)/Fx
+// = -PixelLengthY*(Rx/Ry)/Fx.
+//
+// Given c on a certain row, its pixel directly on its left (col), and the distance (offset) to it, can be found:
+// col = floor(c)
+// offset = c - col
+//
+// The index of this pixel is
+// volumeIndex = row * colCount + col
+//
+//
+// Mainly horizontal rays (|Rx|<=|Ry|) are handled in a similar fashion:
+//
+// E = (WindowMinX + PixelLengthX/2, WindowMaxY - PixelLengthY/2),
+// F = (0, -PixelLengthX)
+//
+// a = (Ex + b*Fx - Dx)/Rx = (Ex - Dx)/Rx
+// b = (Dy + a*Ry - Ey)/Fy = (Dy + (Ex - Dx)*Ry/Rx - Ey)/Fy
+// r = b
+// deltar = PixelLengthX*(Ry/Rx)/Fy.
+// row = floor(r+1/2)
+// offset = r - row
+//
+template <typename Policy>
+void CParallelBeamBlobKernelProjector2D::projectBlock_internal(int _iProjFrom, int _iProjTo, int _iDetFrom, int _iDetTo, Policy& p)
+{
+ // get vector geometry
+ const CParallelVecProjectionGeometry2D* pVecProjectionGeometry;
+ if (dynamic_cast<CParallelProjectionGeometry2D*>(m_pProjectionGeometry)) {
+ pVecProjectionGeometry = dynamic_cast<CParallelProjectionGeometry2D*>(m_pProjectionGeometry)->toVectorGeometry();
+ } else {
+ pVecProjectionGeometry = dynamic_cast<CParallelVecProjectionGeometry2D*>(m_pProjectionGeometry);
+ }
- // POLICY: RAY PRIOR
- if (!p.rayPrior(iRayIndex)) return;
+ // precomputations
+ const float32 pixelLengthX = m_pVolumeGeometry->getPixelLengthX();
+ const float32 pixelLengthY = m_pVolumeGeometry->getPixelLengthY();
+ const float32 inv_pixelLengthX = 1.0f / m_pVolumeGeometry->getPixelLengthX();
+ const float32 inv_pixelLengthY = 1.0f / m_pVolumeGeometry->getPixelLengthY();
+ const int colCount = m_pVolumeGeometry->getGridColCount();
+ const int rowCount = m_pVolumeGeometry->getGridRowCount();
+ const int detCount = pVecProjectionGeometry->getDetectorCount();
+
+ // loop angles
+ #pragma omp parallel for
+ for (int iAngle = _iProjFrom; iAngle < _iProjTo; ++iAngle) {
+
+ // variables
+ float32 Dx, Dy, Ex, Ey, c, r, deltac, deltar, offset, invBlobExtent, RxOverRy, RyOverRx;
+ int iVolumeIndex, iRayIndex, row, col, iDetector;
+ int col_left, col_right, row_top, row_bottom, index;
+
+ const SParProjection * proj = &pVecProjectionGeometry->getProjectionVectors()[iAngle];
+
+ bool vertical = fabs(proj->fRayX) < fabs(proj->fRayY);
+ if (vertical) {
+ RxOverRy = proj->fRayX/proj->fRayY;
+ deltac = -m_pVolumeGeometry->getPixelLengthY() * (proj->fRayX/proj->fRayY) * inv_pixelLengthX;
+ invBlobExtent = m_pVolumeGeometry->getPixelLengthY() / abs(m_fBlobSize * sqrt(proj->fRayY*proj->fRayY + proj->fRayX*proj->fRayX) / proj->fRayY);
+ } else {
+ RyOverRx = proj->fRayY/proj->fRayX;
+ deltar = -m_pVolumeGeometry->getPixelLengthX() * (proj->fRayY/proj->fRayX) * inv_pixelLengthY;
+ invBlobExtent = m_pVolumeGeometry->getPixelLengthX() / abs(m_fBlobSize * sqrt(proj->fRayY*proj->fRayY + proj->fRayX*proj->fRayX) / proj->fRayX);
+ }
- // get values
- float32 t = m_pProjectionGeometry->indexToDetectorOffset(_iDetector);
- float32 theta = m_pProjectionGeometry->getProjectionAngle(_iProjection);
- if (theta >= 7*PIdiv4) theta -= 2*PI;
+ Ex = m_pVolumeGeometry->getWindowMinY() + pixelLengthX*0.5f;
+ Ey = m_pVolumeGeometry->getWindowMaxY() - pixelLengthY*0.5f;
- bool flip = false;
+ // loop detectors
+ for (iDetector = _iDetFrom; iDetector < _iDetTo; ++iDetector) {
+
+ iRayIndex = iAngle * m_pProjectionGeometry->getDetectorCount() + iDetector;
- if (theta >= 3*PIdiv4) {
- theta -= PI;
- t = -t;
- flip = true;
- }
+ // POLICY: RAY PRIOR
+ if (!p.rayPrior(iRayIndex)) continue;
+
+ Dx = proj->fDetSX + (iDetector+0.5f) * proj->fDetUX;
+ Dy = proj->fDetSY + (iDetector+0.5f) * proj->fDetUY;
+ // vertically
+ if (vertical) {
- if (theta <= PIdiv4) { // -pi/4 <= theta <= pi/4
+ // calculate c for row 0
+ c = (Dx + (Ey - Dy)*RxOverRy - Ex) * inv_pixelLengthX;
- // precalculate sin, cos, 1/cos
- float32 sin_theta = sin(theta);
- float32 cos_theta = cos(theta);
- float32 inv_cos_theta = 1.0f / cos_theta;
+ // loop rows
+ for (row = 0; row < rowCount; ++row, c += deltac) {
- // precalculate other stuff
- float32 lengthPerRow = m_pVolumeGeometry->getPixelLengthY() * inv_cos_theta;
- float32 updatePerRow = sin_theta * lengthPerRow;
- float32 inv_pixelLengthX = 1.0f / m_pVolumeGeometry->getPixelLengthX();
- float32 pixelLengthX_over_blobSize = m_pVolumeGeometry->getPixelLengthX() / m_fBlobSize;
-
- // some variables
- int row, col, xmin, xmax;
- float32 P, x, d;
+ col_left = int(c - 0.5f - m_fBlobSize);
+ col_right = int(c + 0.5f + m_fBlobSize);
- // calculate P and x for row 0
- P = (t - sin_theta * m_pVolumeGeometry->pixelRowToCenterY(0)) * inv_cos_theta;
- x = (P - m_pVolumeGeometry->getWindowMinX()) * inv_pixelLengthX - 0.5f;
+ if (col_left < 0) col_left = 0;
+ if (col_right > colCount-1) col_right = colCount-1;
- // for each row
- for (row = 0; row < m_pVolumeGeometry->getGridRowCount(); ++row) {
-
- // calculate extent
- xmin = (int)ceil((P - m_fBlobSize - m_pVolumeGeometry->getWindowMinX()) * inv_pixelLengthX - 0.5f);
- xmax = (int)floor((P + m_fBlobSize - m_pVolumeGeometry->getWindowMinX()) * inv_pixelLengthX - 0.5f);
-
- // add pixels
- for (col = xmin; col <= xmax; col++) {
- if (col >= 0 && col < m_pVolumeGeometry->getGridColCount()) {
- //d = abs(x - col) * pixelLengthX_over_blobSize;
- //index = (int)(d*m_iBlobSampleCount+0.5f);
- //float32 fWeight = m_pfBlobValues[min(index,m_iBlobSampleCount-1)] * lengthPerRow;
-
- float32 fWeight;
- int index;
- if ((x >= col) ^ flip) {
- d = abs(x - col) * pixelLengthX_over_blobSize * cos_theta;
- index = (int)(d*m_iBlobSampleCount+0.5f);
- fWeight = m_pfBlobValues[min(index,m_iBlobSampleCount-1)];
- } else {
- d = abs(x - col) * pixelLengthX_over_blobSize * cos_theta;
- index = (int)(d*m_iBlobSampleCount+0.5f);
- fWeight = m_pfBlobValuesNeg[min(index,m_iBlobSampleCount-1)];
- }
+ // loop columns
+ for (col = col_left; col <= col_right; ++col) {
- int iVolumeIndex = m_pVolumeGeometry->pixelRowColToIndex(row, col);
- // POLICY: PIXEL PRIOR + ADD + POSTERIOR
- if (p.pixelPrior(iVolumeIndex)) {
- p.addWeight(iRayIndex, iVolumeIndex, fWeight);
- p.pixelPosterior(iVolumeIndex);
+ iVolumeIndex = row * colCount + col;
+ // POLICY: PIXEL PRIOR + ADD + POSTERIOR
+ if (p.pixelPrior(iVolumeIndex)) {
+ offset = abs(c - float32(col)) * invBlobExtent;
+ index = (int)(offset*m_iBlobSampleCount+0.5f);
+ p.addWeight(iRayIndex, iVolumeIndex, m_pfBlobValues[min(index,m_iBlobSampleCount-1)]);
+ p.pixelPosterior(iVolumeIndex);
+ }
}
}
}
- // update P and x
- P += updatePerRow;
- x += updatePerRow * inv_pixelLengthX;
- }
+ // horizontally
+ else {
- } else { // pi/4 < theta < 3pi/4
-
- // precalculate sin cos
- float32 sin_90_theta = sin(PIdiv2-theta);
- float32 cos_90_theta = cos(PIdiv2-theta);
- float32 inv_cos_90_theta = 1.0f / cos_90_theta;
-
- // precalculate other stuff
- float32 lengthPerCol = m_pVolumeGeometry->getPixelLengthX() * inv_cos_90_theta;
- float32 updatePerCol = sin_90_theta * lengthPerCol;
- float32 inv_pixelLengthY = 1.0f / m_pVolumeGeometry->getPixelLengthY();
- float32 pixelLengthY_over_blobSize = m_pVolumeGeometry->getPixelLengthY() / m_fBlobSize;
-
- // some variables
- int row, col, xmin, xmax;
- float32 P,x, d;
-
- // calculate P and x for col 0
- P = (sin_90_theta * m_pVolumeGeometry->pixelColToCenterX(0) - t) * inv_cos_90_theta;
- x = (P - m_pVolumeGeometry->getWindowMinY()) * inv_pixelLengthY - 0.5f;
-
- // for each col
- for (col = 0; col < m_pVolumeGeometry->getGridColCount(); ++col) {
-
- // calculate extent
- xmin = (int)ceil((P - m_fBlobSize - m_pVolumeGeometry->getWindowMinY()) * inv_pixelLengthY - 0.5f);
- xmax = (int)floor((P + m_fBlobSize - m_pVolumeGeometry->getWindowMinY()) * inv_pixelLengthY - 0.5f);
-
- // add pixels
- for (row = xmin; row <= xmax; row++) {
- if (row >= 0 && row < m_pVolumeGeometry->getGridRowCount()) {
- //d = abs(x - row) * pixelLengthY_over_blobSize;
- //int index = (int)(d*m_iBlobSampleCount+0.5f);
- //float32 fWeight = m_pfBlobValues[min(index,m_iBlobSampleCount-1)] * lengthPerCol;
-
- float32 fWeight;
- int index;
- if ((x <= row) ^ flip) {
- d = abs(x - row) * pixelLengthY_over_blobSize * cos_90_theta;
- index = (int)(d*m_iBlobSampleCount+0.5f);
- fWeight = m_pfBlobValues[min(index,m_iBlobSampleCount-1)];
- } else {
- d = abs(x - row) * pixelLengthY_over_blobSize * cos_90_theta;
- index = (int)(d*m_iBlobSampleCount+0.5f);
- fWeight = m_pfBlobValuesNeg[min(index,m_iBlobSampleCount-1)];
- }
-
-
- int iVolumeIndex = m_pVolumeGeometry->pixelRowColToIndex(row, col);
- // POLICY: PIXEL PRIOR + ADD + POSTERIOR
- if (p.pixelPrior(iVolumeIndex)) {
- p.addWeight(iRayIndex, iVolumeIndex, fWeight);
- p.pixelPosterior(iVolumeIndex);
- }
- }
- }
+ // calculate r for col 0
+ r = -(Dy + (Ex - Dx)*RyOverRx - Ey) * inv_pixelLengthY;
- // update P and x
- P += updatePerCol;
- x += updatePerCol * inv_pixelLengthY;
- }
+ // loop columns
+ for (col = 0; col < colCount; ++col, r += deltar) {
- }
+ row_top = int(r - 0.5f - m_fBlobSize);
+ row_bottom = int(r + 0.5f + m_fBlobSize);
- // POLICY: RAY POSTERIOR
- p.rayPosterior(iRayIndex);
+ if (row_top < 0) row_top = 0;
+ if (row_bottom > rowCount-1) row_bottom = rowCount-1;
+ // loop rows
+ for (row = row_top; row <= row_bottom; ++row) {
+ iVolumeIndex = row * colCount + col;
+ // POLICY: PIXEL PRIOR + ADD + POSTERIOR
+ if (p.pixelPrior(iVolumeIndex)) {
+ offset = abs(r - float32(row)) * invBlobExtent;
+ index = (int)(offset*m_iBlobSampleCount+0.5f);
+ p.addWeight(iRayIndex, iVolumeIndex, m_pfBlobValues[min(index,m_iBlobSampleCount-1)]);
+ p.pixelPosterior(iVolumeIndex);
+ }
+ }
+ }
+ }
+
+ // POLICY: RAY POSTERIOR
+ p.rayPosterior(iRayIndex);
+
+ } // end loop detector
+ } // end loop angles
}
diff --git a/include/astra/ParallelBeamLineKernelProjector2D.h b/include/astra/ParallelBeamLineKernelProjector2D.h
index 24f43d5..5a10021 100644
--- a/include/astra/ParallelBeamLineKernelProjector2D.h
+++ b/include/astra/ParallelBeamLineKernelProjector2D.h
@@ -30,6 +30,7 @@ $Id$
#define _INC_ASTRA_PARALLELBEAMLINEKERNELPROJECTOR
#include "ParallelProjectionGeometry2D.h"
+#include "ParallelVecProjectionGeometry2D.h"
#include "Float32Data2D.h"
#include "Projector2D.h"
@@ -180,6 +181,7 @@ protected:
template <typename Policy>
void projectBlock_internal(int _iProjFrom, int _iProjTo,
int _iDetFrom, int _iDetTo, Policy& _policy);
+
};
inline std::string CParallelBeamLineKernelProjector2D::getType()
diff --git a/include/astra/ParallelBeamLineKernelProjector2D.inl b/include/astra/ParallelBeamLineKernelProjector2D.inl
index c11f243..199cfd7 100644
--- a/include/astra/ParallelBeamLineKernelProjector2D.inl
+++ b/include/astra/ParallelBeamLineKernelProjector2D.inl
@@ -25,12 +25,13 @@ along with the ASTRA Toolbox. If not, see <http://www.gnu.org/licenses/>.
-----------------------------------------------------------------------
$Id$
*/
+#define policy_weight(p,rayindex,volindex,weight) if (p.pixelPrior(volindex)) { p.addWeight(rayindex, volindex, weight); p.pixelPosterior(volindex); }
template <typename Policy>
void CParallelBeamLineKernelProjector2D::project(Policy& p)
{
projectBlock_internal(0, m_pProjectionGeometry->getProjectionAngleCount(),
- 0, m_pProjectionGeometry->getDetectorCount(), p);
+ 0, m_pProjectionGeometry->getDetectorCount(), p);
}
template <typename Policy>
@@ -49,235 +50,265 @@ void CParallelBeamLineKernelProjector2D::projectSingleRay(int _iProjection, int
//----------------------------------------------------------------------------------------
-// PROJECT BLOCK
+// PROJECT BLOCK - vector projection geometry
+//
+// Kernel limitations: isotropic pixels (PixelLengthX == PixelLengthY)
+//
+// For each angle/detector pair:
+//
+// Let D=(Dx,Dy) denote the centre of the detector (point) in volume coordinates, and
+// let R=(Rx,Ry) denote the direction of the ray (vector).
+//
+// For mainly vertical rays (|Rx|<=|Ry|),
+// let E=(Ex,Ey) denote the centre of the most upper left pixel:
+// E = (WindowMinX + PixelLengthX/2, WindowMaxY - PixelLengthY/2),
+// and let F=(Fx,Fy) denote a vector to the next pixel
+// F = (PixelLengthX, 0)
+//
+// The intersection of the ray (D+aR) with the centre line of the upper row of pixels (E+bF) is
+// { Dx + a*Rx = Ex + b*Fx
+// { Dy + a*Ry = Ey + b*Fy
+// Solving for (a,b) results in:
+// a = (Ey + b*Fy - Dy)/Ry
+// = (Ey - Dy)/Ry
+// b = (Dx + a*Rx - Ex)/Fx
+// = (Dx + (Ey - Dy)*Rx/Ry - Ex)/Fx
+//
+// Define c as the x-value of the intersection of the ray with the upper row in pixel coordinates.
+// c = b
+//
+// The intersection of the ray (D+aR) with the centre line of the second row of pixels (E'+bF) with
+// E'=(WindowMinX + PixelLengthX/2, WindowMaxY - 3*PixelLengthY/2)
+// expressed in x-value pixel coordinates is
+// c' = (Dx + (Ey' - Dy)*Rx/Ry - Ex)/Fx.
+// And thus:
+// deltac = c' - c = (Dx + (Ey' - Dy)*Rx/Ry - Ex)/Fx - (Dx + (Ey - Dy)*Rx/Ry - Ex)/Fx
+// = [(Ey' - Dy)*Rx/Ry - (Ey - Dy)*Rx/Ry]/Fx
+// = [Ey' - Ey]*(Rx/Ry)/Fx
+// = [Ey' - Ey]*(Rx/Ry)/Fx
+// = -PixelLengthY*(Rx/Ry)/Fx.
+//
+// Given c on a certain row, its closest pixel (col), and the distance (offset) to it, can be found:
+// col = floor(c+1/2)
+// offset = c - col
+//
+// The index of this pixel is
+// volumeIndex = row * colCount + col
+//
+// The projection kernel is defined by
+//
+// _____ LengthPerRow
+// /| | |\
+// / | | | \
+// __/ | | | \__ 0
+// -T -S 0 S T
+//
+// with S = 1/2 - 1/2*|Rx/Ry|, T = 1/2 + 1/2*|Rx/Ry|, and LengthPerRow = pixelLengthX * sqrt(Rx^2+Ry^2) / |Ry|
+//
+// And thus
+// { (offset+T)/(T-S) * LengthPerRow if -T <= offset < S
+// W_(rayIndex,volIndex) = { LengthPerRow if -S <= offset <= S
+// { (offset-S)/(T-S) * LengthPerRow if S < offset <= T
+//
+// If -T <= offset < S, the weight for the pixel directly to the left is
+// W_(rayIndex,volIndex-1) = LengthPerRow - (offset+T)/(T-S) * LengthPerRow,
+// and if S < offset <= T, the weight for the pixel directly to the right is
+// W_(rayIndex,volIndex+1) = LengthPerRow - (offset-S)/(T-S) * LengthPerRow.
+//
+//
+// Mainly horizontal rays (|Rx|<=|Ry|) are handled in a similar fashion:
+//
+// E = (WindowMinX + PixelLengthX/2, WindowMaxY - PixelLengthY/2),
+// F = (0, -PixelLengthX)
+//
+// a = (Ex + b*Fx - Dx)/Rx = (Ex - Dx)/Rx
+// b = (Dy + a*Ry - Ey)/Fy = (Dy + (Ex - Dx)*Ry/Rx - Ey)/Fy
+// r = b
+// deltar = PixelLengthX*(Ry/Rx)/Fy.
+// row = floor(r+1/2)
+// offset = r - row
+// S = 1/2 - 1/2*|Ry/Rx|
+// T = 1/2 + 1/2*|Ry/Rx|
+// LengthPerCol = pixelLengthY * sqrt(Rx^2+Ry^2) / |Rx|
+//
+// { (offset+T)/(T-S) * LengthPerCol if -T <= offset < S
+// W_(rayIndex,volIndex) = { LengthPerCol if -S <= offset <= S
+// { (offset-S)/(T-S) * LengthPerCol if S < offset <= T
+//
+// W_(rayIndex,volIndex-colcount) = LengthPerCol - (offset+T)/(T-S) * LengthPerCol
+// W_(rayIndex,volIndex+colcount) = LengthPerCol - (offset-S)/(T-S) * LengthPerCol
+//
template <typename Policy>
void CParallelBeamLineKernelProjector2D::projectBlock_internal(int _iProjFrom, int _iProjTo, int _iDetFrom, int _iDetTo, Policy& p)
{
- // variables
- float32 theta, sin_theta, cos_theta, inv_sin_theta, inv_cos_theta, S, T, t, I, P, x, x2;
- float32 lengthPerRow, updatePerRow, inv_pixelLengthX, lengthPerCol, updatePerCol, inv_pixelLengthY;
- int iVolumeIndex, iRayIndex, row, col, iAngle, iDetector, x1;
- bool switch_t;
+ // get vector geometry
+ const CParallelVecProjectionGeometry2D* pVecProjectionGeometry;
+ if (dynamic_cast<CParallelProjectionGeometry2D*>(m_pProjectionGeometry)) {
+ pVecProjectionGeometry = dynamic_cast<CParallelProjectionGeometry2D*>(m_pProjectionGeometry)->toVectorGeometry();
+ } else {
+ pVecProjectionGeometry = dynamic_cast<CParallelVecProjectionGeometry2D*>(m_pProjectionGeometry);
+ }
+
+ // precomputations
+ const float32 pixelLengthX = m_pVolumeGeometry->getPixelLengthX();
+ const float32 pixelLengthY = m_pVolumeGeometry->getPixelLengthY();
+ const float32 inv_pixelLengthX = 1.0f / pixelLengthX;
+ const float32 inv_pixelLengthY = 1.0f / pixelLengthY;
+ const int colCount = m_pVolumeGeometry->getGridColCount();
+ const int rowCount = m_pVolumeGeometry->getGridRowCount();
+ const int detCount = pVecProjectionGeometry->getDetectorCount();
// loop angles
- for (iAngle = _iProjFrom; iAngle < _iProjTo; ++iAngle) {
-
- // get theta
- theta = m_pProjectionGeometry->getProjectionAngle(iAngle);
- switch_t = false;
- if (theta >= 7*PIdiv4) theta -= 2*PI;
- if (theta >= 3*PIdiv4) {
- theta -= PI;
- switch_t = true;
+ #pragma omp parallel for
+ for (int iAngle = _iProjFrom; iAngle < _iProjTo; ++iAngle) {
+
+ // variables
+ float32 Dx, Dy, Ex, Ey, S, T, weight, c, r, deltac, deltar, offset;
+ float32 RxOverRy, RyOverRx, lengthPerRow, lengthPerCol, invTminSTimesLengthPerRow, invTminSTimesLengthPerCol;
+ int iVolumeIndex, iRayIndex, row, col, iDetector;
+
+ const SParProjection * proj = &pVecProjectionGeometry->getProjectionVectors()[iAngle];
+
+ bool vertical = fabs(proj->fRayX) < fabs(proj->fRayY);
+ if (vertical) {
+ RxOverRy = proj->fRayX/proj->fRayY;
+ lengthPerRow = pixelLengthX * sqrt(proj->fRayY*proj->fRayY + proj->fRayX*proj->fRayX) / abs(proj->fRayY);
+ deltac = -pixelLengthY * RxOverRy * inv_pixelLengthX;
+ S = 0.5f - 0.5f*fabs(RxOverRy);
+ T = 0.5f + 0.5f*fabs(RxOverRy);
+ invTminSTimesLengthPerRow = lengthPerRow / (T - S);
+ } else {
+ RyOverRx = proj->fRayY/proj->fRayX;
+ lengthPerCol = pixelLengthY * sqrt(proj->fRayY*proj->fRayY + proj->fRayX*proj->fRayX) / abs(proj->fRayX);
+ deltar = -pixelLengthX * RyOverRx * inv_pixelLengthY;
+ S = 0.5f - 0.5f*fabs(RyOverRx);
+ T = 0.5f + 0.5f*fabs(RyOverRx);
+ invTminSTimesLengthPerCol = lengthPerCol / (T - S);
}
- // precalculate sin, cos, 1/cos
- sin_theta = sin(theta);
- cos_theta = cos(theta);
- inv_sin_theta = 1.0f / sin_theta;
- inv_cos_theta = 1.0f / cos_theta;
-
- // precalculate kernel limits
- lengthPerRow = m_pVolumeGeometry->getPixelLengthY() * inv_cos_theta;
- updatePerRow = sin_theta * inv_cos_theta;
- inv_pixelLengthX = 1.0f / m_pVolumeGeometry->getPixelLengthX();
-
- // precalculate kernel limits
- lengthPerCol = m_pVolumeGeometry->getPixelLengthX() * inv_sin_theta;
- updatePerCol = cos_theta * inv_sin_theta;
- inv_pixelLengthY = 1.0f / m_pVolumeGeometry->getPixelLengthY();
-
- // precalculate S and T
- S = 0.5f - 0.5f * ((updatePerRow < 0) ? -updatePerRow : updatePerRow);
- T = 0.5f - 0.5f * ((updatePerCol < 0) ? -updatePerCol : updatePerCol);
+ Ex = m_pVolumeGeometry->getWindowMinY() + pixelLengthX*0.5f;
+ Ey = m_pVolumeGeometry->getWindowMaxY() - pixelLengthY*0.5f;
// loop detectors
- for (iDetector = _iDetFrom; iDetector < _iDetTo; ++iDetector) {
-
+ for (int iDetector = _iDetFrom; iDetector < _iDetTo; ++iDetector) {
+
iRayIndex = iAngle * m_pProjectionGeometry->getDetectorCount() + iDetector;
// POLICY: RAY PRIOR
if (!p.rayPrior(iRayIndex)) continue;
-
- // get t
- t = m_pProjectionGeometry->indexToDetectorOffset(iDetector);
- if (switch_t) t = -t;
- // vertically
- if (theta <= PIdiv4) {
-
- // calculate x for row 0
- P = (t - sin_theta * m_pVolumeGeometry->pixelRowToCenterY(0)) * inv_cos_theta;
- x = (P - m_pVolumeGeometry->getWindowMinX()) * inv_pixelLengthX;
+ Dx = proj->fDetSX + (iDetector+0.5f) * proj->fDetUX;
+ Dy = proj->fDetSY + (iDetector+0.5f) * proj->fDetUY;
- // get coords
- int nextx1 = int((x > 0.0f) ? x : x-1.0f);
- float nextx2 = x - nextx1;
-
- // for each row
- for (row = 0; row < m_pVolumeGeometry->getGridRowCount(); ++row) {
+ bool isin = false;
+
+ // vertically
+ if (vertical) {
- x1 = nextx1;
- x2 = nextx2;
+ // calculate c for row 0
+ c = (Dx + (Ey - Dy)*RxOverRy - Ex) * inv_pixelLengthX;
- nextx2 += updatePerRow;
- while (nextx2 >= 1.0f) {
- nextx2 -= 1.0f;
- nextx1++;
- }
- while (nextx2 < 0.0f) {
- nextx2 += 1.0f;
- nextx1--;
- }
+ // loop rows
+ for (row = 0; row < rowCount; ++row, c += deltac) {
- if (x1 < -1 || x1 > m_pVolumeGeometry->getGridColCount()) continue;
+ col = int(c+0.5f);
+ if (col <= 0 || col >= colCount-1) { if (!isin) continue; else break; }
+ offset = c - float32(col);
// left
- if (x2 < 0.5f-S) {
- I = (0.5f - S + x2) / (1.0f - 2.0f*S) * lengthPerRow;
-
- if (x1-1 >= 0 && x1-1 < m_pVolumeGeometry->getGridColCount()) {
- iVolumeIndex = m_pVolumeGeometry->pixelRowColToIndex(row, x1-1);
- // POLICY: PIXEL PRIOR + ADD + POSTERIOR
- if (p.pixelPrior(iVolumeIndex)) {
- p.addWeight(iRayIndex, iVolumeIndex, lengthPerRow-I);
- p.pixelPosterior(iVolumeIndex);
- }
+ if (offset < -S) {
+ weight = (offset + T) * invTminSTimesLengthPerRow;
+
+ iVolumeIndex = row * colCount + col - 1;
+ // POLICY: PIXEL PRIOR + ADD + POSTERIOR
+ if (p.pixelPrior(iVolumeIndex)) {
+ p.addWeight(iRayIndex, iVolumeIndex, lengthPerRow-weight);
+ p.pixelPosterior(iVolumeIndex);
}
- if (x1 >= 0 && x1 < m_pVolumeGeometry->getGridColCount()) {
- iVolumeIndex = m_pVolumeGeometry->pixelRowColToIndex(row, x1);
- // POLICY: PIXEL PRIOR + ADD + POSTERIOR
- if (p.pixelPrior(iVolumeIndex)) {
- p.addWeight(iRayIndex, iVolumeIndex, I);
- p.pixelPosterior(iVolumeIndex);
- }
+ iVolumeIndex++;
+ // POLICY: PIXEL PRIOR + ADD + POSTERIOR
+ if (p.pixelPrior(iVolumeIndex)) {
+ p.addWeight(iRayIndex, iVolumeIndex, weight);
+ p.pixelPosterior(iVolumeIndex);
}
}
- // center
- else if (x2 <= 0.5f+S) {
- if (x1 >= 0 && x1 < m_pVolumeGeometry->getGridColCount()) {
- iVolumeIndex = m_pVolumeGeometry->pixelRowColToIndex(row, x1);
- // POLICY: PIXEL PRIOR + ADD + POSTERIOR
- if (p.pixelPrior(iVolumeIndex)) {
- p.addWeight(iRayIndex, iVolumeIndex, lengthPerRow);
- p.pixelPosterior(iVolumeIndex);
- }
- }
- }
-
// right
- else {
- I = (1.5f - S - x2) / (1.0f - 2.0f*S) * lengthPerRow;
-
- if (x1 >= 0 && x1 < m_pVolumeGeometry->getGridColCount()) {
- iVolumeIndex = m_pVolumeGeometry->pixelRowColToIndex(row, x1);
- // POLICY: PIXEL PRIOR + ADD + POSTERIOR
- if (p.pixelPrior(iVolumeIndex)) {
- p.addWeight(iRayIndex, iVolumeIndex, I);
- p.pixelPosterior(iVolumeIndex);
- }
+ else if (S < offset) {
+ weight = (offset - S) * invTminSTimesLengthPerRow;
+
+ iVolumeIndex = row * colCount + col;
+ // POLICY: PIXEL PRIOR + ADD + POSTERIOR
+ if (p.pixelPrior(iVolumeIndex)) {
+ p.addWeight(iRayIndex, iVolumeIndex, lengthPerRow-weight);
+ p.pixelPosterior(iVolumeIndex);
+ }
+
+ iVolumeIndex++;
+ // POLICY: PIXEL PRIOR + ADD + POSTERIOR
+ if (p.pixelPrior(iVolumeIndex)) {
+ p.addWeight(iRayIndex, iVolumeIndex, weight);
+ p.pixelPosterior(iVolumeIndex);
}
- if (x1+1 >= 0 && x1+1 < m_pVolumeGeometry->getGridColCount()) {
- iVolumeIndex = m_pVolumeGeometry->pixelRowColToIndex(row, x1+1);
- // POLICY: PIXEL PRIOR + ADD + POSTERIOR
- if (p.pixelPrior(iVolumeIndex)) {
- p.addWeight(iRayIndex, iVolumeIndex, lengthPerRow-I);
- p.pixelPosterior(iVolumeIndex);
- }
+ }
+
+ // centre
+ else {
+ iVolumeIndex = row * colCount + col;
+ // POLICY: PIXEL PRIOR + ADD + POSTERIOR
+ if (p.pixelPrior(iVolumeIndex)) {
+ p.addWeight(iRayIndex, iVolumeIndex, lengthPerRow);
+ p.pixelPosterior(iVolumeIndex);
}
}
+ isin = true;
}
}
// horizontally
- else if (PIdiv4 <= theta && theta <= 3*PIdiv4) {
+ else {
- // calculate point P
- P = (t - cos_theta * m_pVolumeGeometry->pixelColToCenterX(0)) * inv_sin_theta;
- x = (m_pVolumeGeometry->getWindowMaxY() - P) * inv_pixelLengthY;
+ // calculate r for col 0
+ r = -(Dy + (Ex - Dx)*RyOverRx - Ey) * inv_pixelLengthY;
- // get coords
- int nextx1 = int((x > 0.0f) ? x : x-1.0f);
- float nextx2 = x - nextx1;
+ // loop columns
+ for (col = 0; col < colCount; ++col, r += deltar) {
- // for each col
- for (col = 0; col < m_pVolumeGeometry->getGridColCount(); ++col) {
+ row = int(r+0.5f);
+ if (row <= 0 || row >= rowCount-1) { if (!isin) continue; else break; }
+ offset = r - float32(row);
- x1 = nextx1;
- x2 = nextx2;
+ // up
+ if (offset < -S) {
+ weight = (offset + T) * invTminSTimesLengthPerCol;
- nextx2 += updatePerCol;
- while (nextx2 >= 1.0f) {
- nextx2 -= 1.0f;
- nextx1++;
- }
- while (nextx2 < 0.0f) {
- nextx2 += 1.0f;
- nextx1--;
- }
+ iVolumeIndex = (row-1) * colCount + col;
+ policy_weight(p, iRayIndex, iVolumeIndex, lengthPerCol-weight)
- if (x1 < -1 || x1 > m_pVolumeGeometry->getGridRowCount()) continue;
+ iVolumeIndex += colCount;
+ policy_weight(p, iRayIndex, iVolumeIndex, weight)
+ }
- // up
- if (x2 < 0.5f-T) {
- I = (0.5f - T + x2) / (1.0f - 2.0f*T) * lengthPerCol;
-
- if (x1-1 >= 0 && x1-1 < m_pVolumeGeometry->getGridRowCount()) {
- iVolumeIndex = m_pVolumeGeometry->pixelRowColToIndex(x1-1, col);
- // POLICY: PIXEL PRIOR + ADD + POSTERIOR
- if (p.pixelPrior(iVolumeIndex)) {
- p.addWeight(iRayIndex, iVolumeIndex, lengthPerCol-I);
- p.pixelPosterior(iVolumeIndex);
- }
- }
+ // down
+ else if (S < offset) {
+ weight = (offset - S) * invTminSTimesLengthPerCol;
- if (x1 >= 0 && x1 < m_pVolumeGeometry->getGridRowCount()) {
- iVolumeIndex = m_pVolumeGeometry->pixelRowColToIndex(x1, col);
- // POLICY: PIXEL PRIOR + ADD + POSTERIOR
- if (p.pixelPrior(iVolumeIndex)) {
- p.addWeight(iRayIndex, iVolumeIndex, I);
- p.pixelPosterior(iVolumeIndex);
- }
- }
- }
+ iVolumeIndex = row * colCount + col;
+ policy_weight(p, iRayIndex, iVolumeIndex, lengthPerCol-weight)
- // center
- else if (x2 <= 0.5f+T) {
- if (x1 >= 0 && x1 < m_pVolumeGeometry->getGridRowCount()) {
- iVolumeIndex = m_pVolumeGeometry->pixelRowColToIndex(x1, col);
- // POLICY: PIXEL PRIOR + ADD + POSTERIOR
- if (p.pixelPrior(iVolumeIndex)) {
- p.addWeight(iRayIndex, iVolumeIndex, lengthPerCol);
- p.pixelPosterior(iVolumeIndex);
- }
- }
+ iVolumeIndex += colCount;
+ policy_weight(p, iRayIndex, iVolumeIndex, weight)
}
- // down
- else {
- I = (1.5f - T - x2) / (1.0f - 2.0f*T) * lengthPerCol;
-
- if (x1 >= 0 && x1 < m_pVolumeGeometry->getGridRowCount()) {
- iVolumeIndex = m_pVolumeGeometry->pixelRowColToIndex(x1, col);
- // POLICY: PIXEL PRIOR + ADD + POSTERIOR
- if (p.pixelPrior(iVolumeIndex)) {
- p.addWeight(iRayIndex, iVolumeIndex, I);
- p.pixelPosterior(iVolumeIndex);
- }
- }
- if (x1+1 >= 0 && x1+1 < m_pVolumeGeometry->getGridRowCount()) {
- iVolumeIndex = m_pVolumeGeometry->pixelRowColToIndex(x1+1, col);
- // POLICY: PIXEL PRIOR + ADD + POSTERIOR
- if (p.pixelPrior(iVolumeIndex)) {
- p.addWeight(iRayIndex, iVolumeIndex, lengthPerCol-I);
- p.pixelPosterior(iVolumeIndex);
- }
- }
+ // centre
+ else {
+ iVolumeIndex = row * colCount + col;
+ policy_weight(p, iRayIndex, iVolumeIndex, lengthPerCol)
}
+ isin = true;
}
- } // end loop col
+ }
// POLICY: RAY POSTERIOR
p.rayPosterior(iRayIndex);
@@ -286,4 +317,3 @@ void CParallelBeamLineKernelProjector2D::projectBlock_internal(int _iProjFrom, i
} // end loop angles
}
-
diff --git a/include/astra/ParallelBeamLinearKernelProjector2D.h b/include/astra/ParallelBeamLinearKernelProjector2D.h
index 855093a..8e25c38 100644
--- a/include/astra/ParallelBeamLinearKernelProjector2D.h
+++ b/include/astra/ParallelBeamLinearKernelProjector2D.h
@@ -30,6 +30,7 @@ $Id$
#define _INC_ASTRA_PARALLELLINEARKERNELPROJECTOR
#include "ParallelProjectionGeometry2D.h"
+#include "ParallelVecProjectionGeometry2D.h"
#include "Float32Data2D.h"
#include "Projector2D.h"
@@ -185,7 +186,6 @@ protected:
void projectBlock_internal(int _iProjFrom, int _iProjTo,
int _iDetFrom, int _iDetTo, Policy& _policy);
-
};
//----------------------------------------------------------------------------------------
diff --git a/include/astra/ParallelBeamLinearKernelProjector2D.inl b/include/astra/ParallelBeamLinearKernelProjector2D.inl
index 67e0d58..ecbdeb3 100644
--- a/include/astra/ParallelBeamLinearKernelProjector2D.inl
+++ b/include/astra/ParallelBeamLinearKernelProjector2D.inl
@@ -25,13 +25,13 @@ along with the ASTRA Toolbox. If not, see <http://www.gnu.org/licenses/>.
-----------------------------------------------------------------------
$Id$
*/
-
+#define policy_weight(p,rayindex,volindex,weight) if (p.pixelPrior(volindex)) { p.addWeight(rayindex, volindex, weight); p.pixelPosterior(volindex); }
template <typename Policy>
void CParallelBeamLinearKernelProjector2D::project(Policy& p)
{
projectBlock_internal(0, m_pProjectionGeometry->getProjectionAngleCount(),
- 0, m_pProjectionGeometry->getDetectorCount(), p);
+ 0, m_pProjectionGeometry->getDetectorCount(), p);
}
template <typename Policy>
@@ -48,47 +48,127 @@ void CParallelBeamLinearKernelProjector2D::projectSingleRay(int _iProjection, in
_iDetector, _iDetector + 1, p);
}
+
+
//----------------------------------------------------------------------------------------
-// PROJECT BLOCK
+// PROJECT BLOCK - vector projection geometry
+//
+// Kernel limitations: isotropic pixels (PixelLengthX == PixelLengthY)
+//
+// For each angle/detector pair:
+//
+// Let D=(Dx,Dy) denote the centre of the detector (point) in volume coordinates, and
+// let R=(Rx,Ry) denote the direction of the ray (vector).
+//
+// For mainly vertical rays (|Rx|<=|Ry|),
+// let E=(Ex,Ey) denote the centre of the most upper left pixel:
+// E = (WindowMinX + PixelLengthX/2, WindowMaxY - PixelLengthY/2),
+// and let F=(Fx,Fy) denote a vector to the next pixel
+// F = (PixelLengthX, 0)
+//
+// The intersection of the ray (D+aR) with the centre line of the upper row of pixels (E+bF) is
+// { Dx + a*Rx = Ex + b*Fx
+// { Dy + a*Ry = Ey + b*Fy
+// Solving for (a,b) results in:
+// a = (Ey + b*Fy - Dy)/Ry
+// = (Ey - Dy)/Ry
+// b = (Dx + a*Rx - Ex)/Fx
+// = (Dx + (Ey - Dy)*Rx/Ry - Ex)/Fx
+//
+// Define c as the x-value of the intersection of the ray with the upper row in pixel coordinates.
+// c = b
+//
+// The intersection of the ray (D+aR) with the centre line of the second row of pixels (E'+bF) with
+// E'=(WindowMinX + PixelLengthX/2, WindowMaxY - 3*PixelLengthY/2)
+// expressed in x-value pixel coordinates is
+// c' = (Dx + (Ey' - Dy)*Rx/Ry - Ex)/Fx.
+// And thus:
+// deltac = c' - c = (Dx + (Ey' - Dy)*Rx/Ry - Ex)/Fx - (Dx + (Ey - Dy)*Rx/Ry - Ex)/Fx
+// = [(Ey' - Dy)*Rx/Ry - (Ey - Dy)*Rx/Ry]/Fx
+// = [Ey' - Ey]*(Rx/Ry)/Fx
+// = [Ey' - Ey]*(Rx/Ry)/Fx
+// = -PixelLengthY*(Rx/Ry)/Fx.
+//
+// Given c on a certain row, its pixel directly on its left (col), and the distance (offset) to it, can be found:
+// col = floor(c)
+// offset = c - col
+//
+// The index of this pixel is
+// volumeIndex = row * colCount + col
+//
+// The projection kernel is defined by
+//
+// LengthPerRow
+// /|\
+// / | \
+// __/ | \__ 0
+// p0 p1 p2
+//
+// And thus
+// W_(rayIndex,volIndex) = (1 - offset) * lengthPerRow
+// W_(rayIndex,volIndex+1) = offset * lengthPerRow
+//
+//
+// Mainly horizontal rays (|Rx|<=|Ry|) are handled in a similar fashion:
+//
+// E = (WindowMinX + PixelLengthX/2, WindowMaxY - PixelLengthY/2),
+// F = (0, -PixelLengthX)
+//
+// a = (Ex + b*Fx - Dx)/Rx = (Ex - Dx)/Rx
+// b = (Dy + a*Ry - Ey)/Fy = (Dy + (Ex - Dx)*Ry/Rx - Ey)/Fy
+// r = b
+// deltar = PixelLengthX*(Ry/Rx)/Fy.
+// row = floor(r+1/2)
+// offset = r - row
+// LengthPerCol = pixelLengthY * sqrt(Rx^2+Ry^2) / |Rx|
+//
+// W_(rayIndex,volIndex) = (1 - offset) * lengthPerCol
+// W_(rayIndex,volIndex+colcount) = offset * lengthPerCol
+//
template <typename Policy>
void CParallelBeamLinearKernelProjector2D::projectBlock_internal(int _iProjFrom, int _iProjTo, int _iDetFrom, int _iDetTo, Policy& p)
{
- // variables
- float32 theta, sin_theta, cos_theta, inv_sin_theta, inv_cos_theta, t;
- float32 lengthPerRow, updatePerRow, inv_pixelLengthX;
- float32 lengthPerCol, updatePerCol, inv_pixelLengthY;
- bool switch_t;
- int iAngle, iDetector, iVolumeIndex, iRayIndex;
- int row, col, x1;
- float32 P,x,x2;
+ // get vector geometry
+ const CParallelVecProjectionGeometry2D* pVecProjectionGeometry;
+ if (dynamic_cast<CParallelProjectionGeometry2D*>(m_pProjectionGeometry)) {
+ pVecProjectionGeometry = dynamic_cast<CParallelProjectionGeometry2D*>(m_pProjectionGeometry)->toVectorGeometry();
+ } else {
+ pVecProjectionGeometry = dynamic_cast<CParallelVecProjectionGeometry2D*>(m_pProjectionGeometry);
+ }
+
+ // precomputations
+ const float32 pixelLengthX = m_pVolumeGeometry->getPixelLengthX();
+ const float32 pixelLengthY = m_pVolumeGeometry->getPixelLengthY();
+ const float32 inv_pixelLengthX = 1.0f / pixelLengthX;
+ const float32 inv_pixelLengthY = 1.0f / pixelLengthY;
+ const int colCount = m_pVolumeGeometry->getGridColCount();
+ const int rowCount = m_pVolumeGeometry->getGridRowCount();
+ const int detCount = pVecProjectionGeometry->getDetectorCount();
// loop angles
- for (iAngle = _iProjFrom; iAngle < _iProjTo; ++iAngle) {
-
- // get theta
- theta = m_pProjectionGeometry->getProjectionAngle(iAngle);
- switch_t = false;
- if (theta >= 7*PIdiv4) theta -= 2*PI;
- if (theta >= 3*PIdiv4) {
- theta -= PI;
- switch_t = true;
+ #pragma omp parallel for
+ for (int iAngle = _iProjFrom; iAngle < _iProjTo; ++iAngle) {
+
+ // variables
+ float32 Dx, Dy, Ex, Ey, x, y, c, r, deltac, deltar, offset;
+ float32 RxOverRy, RyOverRx, lengthPerRow, lengthPerCol;
+ int iVolumeIndex, iRayIndex, row, col, iDetector;
+
+ const SParProjection * proj = &pVecProjectionGeometry->getProjectionVectors()[iAngle];
+
+ bool vertical = fabs(proj->fRayX) < fabs(proj->fRayY);
+ if (vertical) {
+ RxOverRy = proj->fRayX/proj->fRayY;
+ lengthPerRow = m_pVolumeGeometry->getPixelLengthX() * sqrt(proj->fRayY*proj->fRayY + proj->fRayX*proj->fRayX) / abs(proj->fRayY);
+ deltac = -pixelLengthY * RxOverRy * inv_pixelLengthX;
+ } else {
+ RyOverRx = proj->fRayY/proj->fRayX;
+ lengthPerCol = m_pVolumeGeometry->getPixelLengthY() * sqrt(proj->fRayY*proj->fRayY + proj->fRayX*proj->fRayX) / abs(proj->fRayX);
+ deltar = -pixelLengthX * RyOverRx * inv_pixelLengthY;
}
- // precalculate sin, cos, 1/cos
- sin_theta = sin(theta);
- cos_theta = cos(theta);
- inv_cos_theta = 1.0f / cos_theta;
- inv_sin_theta = 1.0f / sin_theta;
-
- // precalculate kernel limits
- lengthPerRow = m_pVolumeGeometry->getPixelLengthY() * inv_cos_theta;
- updatePerRow = sin_theta * inv_cos_theta;
- inv_pixelLengthX = 1.0f / m_pVolumeGeometry->getPixelLengthX();
-
- // precalculate kernel limits
- lengthPerCol = m_pVolumeGeometry->getPixelLengthX() * inv_sin_theta;
- updatePerCol = cos_theta * inv_sin_theta;
- inv_pixelLengthY = 1.0f / m_pVolumeGeometry->getPixelLengthY();
+ Ex = m_pVolumeGeometry->getWindowMinY() + pixelLengthX*0.5f;
+ Ey = m_pVolumeGeometry->getWindowMaxY() - pixelLengthY*0.5f;
// loop detectors
for (iDetector = _iDetFrom; iDetector < _iDetTo; ++iDetector) {
@@ -98,79 +178,54 @@ void CParallelBeamLinearKernelProjector2D::projectBlock_internal(int _iProjFrom,
// POLICY: RAY PRIOR
if (!p.rayPrior(iRayIndex)) continue;
- // get t
- t = m_pProjectionGeometry->indexToDetectorOffset(iDetector);
- if (switch_t) {
- t = -t;
- }
+ Dx = proj->fDetSX + (iDetector+0.5f) * proj->fDetUX;
+ Dy = proj->fDetSY + (iDetector+0.5f) * proj->fDetUY;
+
+ bool isin = false;
// vertically
- if (theta <= PIdiv4) {
-
- // calculate x for row 0
- P = (t - sin_theta * m_pVolumeGeometry->pixelRowToCenterY(0)) * inv_cos_theta;
- x = m_pVolumeGeometry->coordXToColF(P) - 0.5f;
+ if (vertical) {
+
+ // calculate c for row 0
+ c = (Dx + (Ey - Dy)*RxOverRy - Ex) * inv_pixelLengthX;
+
+ // loop rows
+ for (row = 0; row < rowCount; ++row, c += deltac) {
- // for each row
- for (row = 0; row < m_pVolumeGeometry->getGridRowCount(); ++row) {
+ col = int(c);
+ if (col <= 0 || col >= colCount-1) { if (!isin) continue; else break; }
+ offset = c - float32(col);
+
+ iVolumeIndex = row * colCount + col;
+ policy_weight(p, iRayIndex, iVolumeIndex, (1.0f - offset) * lengthPerRow)
- // get coords
- x1 = int((x > 0.0f) ? x : x-1.0f);
- x2 = x - x1;
- x += updatePerRow;
-
- // add weights
- if (x1 >= 0 && x1 < m_pVolumeGeometry->getGridColCount()) {
- iVolumeIndex = m_pVolumeGeometry->pixelRowColToIndex(row, x1);
- // POLICY: PIXEL PRIOR + ADD + POSTERIOR
- if (p.pixelPrior(iVolumeIndex)) {
- p.addWeight(iRayIndex, iVolumeIndex, (1.0f - x2) * lengthPerRow);
- p.pixelPosterior(iVolumeIndex);
- }
- }
- if (x1+1 >= 0 && x1+1 < m_pVolumeGeometry->getGridColCount()) {
- iVolumeIndex = m_pVolumeGeometry->pixelRowColToIndex(row, x1+1);
- // POLICY: PIXEL PRIOR + ADD + POSTERIOR
- if (p.pixelPrior(iVolumeIndex)) {
- p.addWeight(iRayIndex, iVolumeIndex, (x2) * lengthPerRow);
- p.pixelPosterior(iVolumeIndex);
- }
- }
+ iVolumeIndex++;
+ policy_weight(p, iRayIndex, iVolumeIndex, offset * lengthPerRow)
+
+ isin = true;
}
}
// horizontally
- else if (PIdiv4 <= theta && theta <= 3*PIdiv4) {
-
- // calculate point P
- P = (t - cos_theta * m_pVolumeGeometry->pixelColToCenterX(0)) * inv_sin_theta;
- x = m_pVolumeGeometry->coordYToRowF(P) - 0.5f;
-
- // for each row
- for (col = 0; col < m_pVolumeGeometry->getGridColCount(); ++col) {
-
- // get coords
- x1 = int((x > 0.0f) ? x : x-1.0f);
- x2 = x - x1;
- x += updatePerCol;
-
- // add weights
- if (x1 >= 0 && x1 < m_pVolumeGeometry->getGridRowCount()) {
- iVolumeIndex = m_pVolumeGeometry->pixelRowColToIndex(x1, col);
- // POLICY: PIXEL PRIOR + ADD + POSTERIOR
- if (p.pixelPrior(iVolumeIndex)) {
- p.addWeight(iRayIndex, iVolumeIndex, (1.0f - x2) * lengthPerCol);
- p.pixelPosterior(iVolumeIndex);
- }
- }
- if (x1+1 >= 0 && x1+1 < m_pVolumeGeometry->getGridRowCount()) {
- iVolumeIndex = m_pVolumeGeometry->pixelRowColToIndex(x1+1, col);
- // POLICY: PIXEL PRIOR + ADD + POSTERIOR
- if (p.pixelPrior(iVolumeIndex)) {
- p.addWeight(iRayIndex, iVolumeIndex, x2 * lengthPerCol);
- p.pixelPosterior(iVolumeIndex);
- }
- }
+ else {
+
+ // calculate r for col 0
+ r = -(Dy + (Ex - Dx)*RyOverRx - Ey) * inv_pixelLengthY;
+
+ // loop columns
+ for (col = 0; col < colCount; ++col, r += deltar) {
+
+ row = int(r);
+ if (row <= 0 || row >= rowCount-1) { if (!isin) continue; else break; }
+ offset = r - float32(row);
+
+ iVolumeIndex = row * colCount + col;
+ policy_weight(p, iRayIndex, iVolumeIndex, (1.0f - offset) * lengthPerCol)
+
+ iVolumeIndex += colCount;
+ policy_weight(p, iRayIndex, iVolumeIndex, offset * lengthPerCol)
+
+ isin = true;
}
}
@@ -179,6 +234,4 @@ void CParallelBeamLinearKernelProjector2D::projectBlock_internal(int _iProjFrom,
} // end loop detector
} // end loop angles
-
}
-
diff --git a/include/astra/ParallelBeamStripKernelProjector2D.h b/include/astra/ParallelBeamStripKernelProjector2D.h
index 624bd3c..8fb665f 100644
--- a/include/astra/ParallelBeamStripKernelProjector2D.h
+++ b/include/astra/ParallelBeamStripKernelProjector2D.h
@@ -30,6 +30,7 @@ $Id$
#define _INC_ASTRA_PARALLELBEAMSTROKEKERNELPROJECTOR
#include "ParallelProjectionGeometry2D.h"
+#include "ParallelVecProjectionGeometry2D.h"
#include "Float32Data2D.h"
#include "Projector2D.h"
diff --git a/include/astra/ParallelBeamStripKernelProjector2D.inl b/include/astra/ParallelBeamStripKernelProjector2D.inl
index 7d73888..f0203f2 100644
--- a/include/astra/ParallelBeamStripKernelProjector2D.inl
+++ b/include/astra/ParallelBeamStripKernelProjector2D.inl
@@ -30,7 +30,7 @@ template <typename Policy>
void CParallelBeamStripKernelProjector2D::project(Policy& p)
{
projectBlock_internal(0, m_pProjectionGeometry->getProjectionAngleCount(),
- 0, m_pProjectionGeometry->getDetectorCount(), p);
+ 0, m_pProjectionGeometry->getDetectorCount(), p);
}
template <typename Policy>
@@ -49,250 +49,242 @@ void CParallelBeamStripKernelProjector2D::projectSingleRay(int _iProjection, int
//----------------------------------------------------------------------------------------
// PROJECT BLOCK
+//
+// Kernel limitations: isotropic pixels (PixelLengthX == PixelLengthY)
+//
+// For each angle/detector pair:
+//
+// Let DL=(DLx,DLy) denote the left of the detector (point) in volume coordinates, and
+// Let DR=(DRx,DRy) denote the right of the detector (point) in volume coordinates, and
+// let R=(Rx,Ry) denote the direction of the ray (vector).
+//
+// For mainly vertical rays (|Rx|<=|Ry|),
+// let E=(Ex,Ey) denote the centre of the most upper left pixel:
+// E = (WindowMinX + PixelLengthX/2, WindowMaxY - PixelLengthY/2),
+// and let F=(Fx,Fy) denote a vector to the next pixel
+// F = (PixelLengthX, 0)
+//
+// The intersection of the left edge of the strip (DL+aR) with the centre line of the upper row of pixels (E+bF) is
+// { DLx + a*Rx = Ex + b*Fx
+// { DLy + a*Ry = Ey + b*Fy
+// Solving for (a,b) results in:
+// a = (Ey + b*Fy - DLy)/Ry
+// = (Ey - DLy)/Ry
+// b = (DLx + a*Rx - Ex)/Fx
+// = (DLx + (Ey - DLy)*Rx/Ry - Ex)/Fx
+//
+// Define cL as the x-value of the intersection of the left edge of the strip with the upper row in pixel coordinates.
+// cL = b
+//
+// cR, the x-value of the intersection of the right edge of the strip with the upper row in pixel coordinates can be found similarly.
+//
+// The intersection of the ray (DL+aR) with the left line of the second row of pixels (E'+bF) with
+// E'=(WindowMinX + PixelLengthX/2, WindowMaxY - 3*PixelLengthY/2)
+// expressed in x-value pixel coordinates is
+// cL' = (DLx + (Ey' - DLy)*Rx/Ry - Ex)/Fx.
+// And thus:
+// deltac = cL' - cL = (DLx + (Ey' - DLy)*Rx/Ry - Ex)/Fx - (DLx + (Ey - DLy)*Rx/Ry - Ex)/Fx
+// = [(Ey' - DLy)*Rx/Ry - (Ey - DLy)*Rx/Ry]/Fx
+// = [Ey' - Ey]*(Rx/Ry)/Fx
+// = [Ey' - Ey]*(Rx/Ry)/Fx
+// = -PixelLengthY*(Rx/Ry)/Fx.
+//
+// The projection weight for a certain pixel is defined by the area between two points of
+//
+// _____ LengthPerRow
+// /| | |\
+// / | | | \
+// __/ | | | \__ 0
+// -T -S 0 S T
+// with S = 1/2 - 1/2*|Rx/Ry|, T = 1/2 + 1/2*|Rx/Ry|, and LengthPerRow = pixelLengthX * sqrt(Rx^2+Ry^2) / |Ry|
+//
+// For a certain row, all columns that are 'hit' by this kernel lie in the interval
+// (col_left, col_right) = (floor(cL-1/2+S), floor(cR+3/2-S))
+//
+// The offsets for both is
+// (offsetL, offsetR) = (cL - floor(col_left), cR - floor(col_left))
+//
+// The projection weight is found by the difference between the integrated values of the kernel
+// offset <= -T Kernel = 0
+// -T < offset <= -S Kernel = PixelArea/2*(T+offset)^2/(T-S)
+// -S < offset <= S Kernel = PixelArea/2 + offset
+// S < offset <= T Kernel = PixelArea - PixelArea/2*(T-offset)^2/(T-S)
+// T <= offset: Kernel = PixelArea
+//
template <typename Policy>
void CParallelBeamStripKernelProjector2D::projectBlock_internal(int _iProjFrom, int _iProjTo, int _iDetFrom, int _iDetTo, Policy& p)
{
- ASTRA_ASSERT(m_bIsInitialized);
-
- // Some variables
- float32 theta, t;
- int row, col;
- int iAngle;
- int iDetector;
- float32 res;
- float32 PL, PLimitL, PLimitR;
- float32 xL, xR, XLimitL, XLimitR;
- int x1L,x1R;
- float32 x2L, x2R, updateX;
- int iVolumeIndex, iRayIndex;
-
- float32 sin_theta, cos_theta, inv_sin_theta, inv_cos_theta;
- float32 fabs_sin_theta, fabs_cos_theta, fabs_inv_sin_theta, fabs_inv_cos_theta;
- float32 PW, PH, DW, inv_PW, inv_PH;
- float32 S, T, U, V, inv_4T;
+ // get vector geometry
+ const CParallelVecProjectionGeometry2D* pVecProjectionGeometry;
+ if (dynamic_cast<CParallelProjectionGeometry2D*>(m_pProjectionGeometry)) {
+ pVecProjectionGeometry = dynamic_cast<CParallelProjectionGeometry2D*>(m_pProjectionGeometry)->toVectorGeometry();
+ } else {
+ pVecProjectionGeometry = dynamic_cast<CParallelVecProjectionGeometry2D*>(m_pProjectionGeometry);
+ }
+
+ // precomputations
+ const float32 pixelLengthX = m_pVolumeGeometry->getPixelLengthX();
+ const float32 pixelLengthY = m_pVolumeGeometry->getPixelLengthY();
+ const float32 pixelArea = pixelLengthX * pixelLengthY;
+ const float32 inv_pixelLengthX = 1.0f / pixelLengthX;
+ const float32 inv_pixelLengthY = 1.0f / pixelLengthY;
+ const int colCount = m_pVolumeGeometry->getGridColCount();
+ const int rowCount = m_pVolumeGeometry->getGridRowCount();
+ const int detCount = pVecProjectionGeometry->getDetectorCount();
// loop angles
- for (iAngle = _iProjFrom; iAngle < _iProjTo; ++iAngle) {
-
- // get values
- theta = m_pProjectionGeometry->getProjectionAngle(iAngle);
- bool switch_t = false;
- if (theta >= 7*PIdiv4) theta -= 2*PI;
- if (theta >= 3*PIdiv4) {
- theta -= PI;
- switch_t = true;
- }
-
- // Precalculate sin, cos, 1/cos
- sin_theta = sin(theta);
- cos_theta = cos(theta);
- inv_cos_theta = 1.0f / cos_theta;
- inv_sin_theta = 1.0f / sin_theta;
-
- fabs_sin_theta = (sin_theta < 0.0f) ? -sin_theta : sin_theta;
- fabs_cos_theta = (cos_theta < 0.0f) ? -cos_theta : cos_theta;
- fabs_inv_cos_theta = (inv_cos_theta < 0.0f) ? -inv_cos_theta : inv_cos_theta;
- fabs_inv_sin_theta = (inv_sin_theta < 0.0f) ? -inv_sin_theta : inv_sin_theta;
-
- // Other precalculations
- PW = m_pVolumeGeometry->getPixelLengthX();
- PH = m_pVolumeGeometry->getPixelLengthY();
- DW = m_pProjectionGeometry->getDetectorWidth();
- inv_PW = 1.0f / PW;
- inv_PH = 1.0f / PH;
-
- // [-45?,45?] and [135?,225?]
- if (theta < PIdiv4) {
-
- // Precalculate kernel limits
- S = -0.5f * fabs_sin_theta * fabs_inv_cos_theta;
- T = -S;
- U = 1.0f + S;
- V = 1.0f - S;
- inv_4T = 0.25f / T;
-
- updateX = sin_theta * inv_cos_theta;
-
- // loop detectors
- for (iDetector = _iDetFrom; iDetector < _iDetTo; ++iDetector) {
-
- iRayIndex = iAngle * m_pProjectionGeometry->getDetectorCount() + iDetector;
-
- // POLICY: RAY PRIOR
- if (!p.rayPrior(iRayIndex)) continue;
-
- // get t
- t = m_pProjectionGeometry->indexToDetectorOffset(iDetector);
- if (switch_t) t = -t;
-
- // calculate left strip extremes (volume coordinates)
- PL = (t - sin_theta * m_pVolumeGeometry->pixelRowToCenterY(0) - DW*0.5f) * inv_cos_theta;
- PLimitL = PL - 0.5f * fabs_sin_theta * fabs_inv_cos_theta * PH;
- PLimitR = PLimitL + DW * inv_cos_theta + PH * fabs_sin_theta * fabs_inv_cos_theta;
-
- // calculate strip extremes (pixel coordinates)
- XLimitL = (PLimitL - m_pVolumeGeometry->getWindowMinX()) * inv_PW;
- XLimitR = (PLimitR - m_pVolumeGeometry->getWindowMinX()) * inv_PW;
- xL = (PL - m_pVolumeGeometry->getWindowMinX()) * inv_PW;
- xR = xL + (DW * inv_cos_theta) * 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;
- XLimitR += updateX;
- xL += updateX;
- xR += updateX;
-
- // 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) res = 1.0f;
- else if (x2R > U) res = x2R - (x2R-U)*(x2R-U)*inv_4T;
- else if (x2R >= T) res = x2R;
- else if (x2R > S) res = (x2R-S)*(x2R-S) * inv_4T;
- else { x2L -= 1.0f; x2R -= 1.0f; continue; }
-
- // left
- if (x2L <= S) {} // - 0.0f
- else if (x2L < T) res -= (x2L-S)*(x2L-S) * inv_4T;
- else if (x2L <= U) res -= x2L;
- else if (x2L < V) res -= x2L - (x2L-U)*(x2L-U)*inv_4T;
- else { x2L -= 1.0f; x2R -= 1.0f; continue; }
-
- // POLICY: ADD
- p.addWeight(iRayIndex, iVolumeIndex, PW*PH * res);
-
- // 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
+ #pragma omp parallel for
+ for (int iAngle = _iProjFrom; iAngle < _iProjTo; ++iAngle) {
+
+ // variables
+ float32 DLx, DLy, DRx, DRy, Ex, Ey, S, T, deltac, deltar, offsetL, offsetR, invTminS;
+ float32 res, RxOverRy, RyOverRx, cL, cR, rL, rR;
+ int iVolumeIndex, iRayIndex, iDetector;
+ int row, row_top, row_bottom, col, col_left, col_right;
+
+ const SParProjection * proj = &pVecProjectionGeometry->getProjectionVectors()[iAngle];
+
+ bool vertical = fabs(proj->fRayX) < fabs(proj->fRayY);
+ if (vertical) {
+ RxOverRy = proj->fRayX/proj->fRayY;
+ deltac = -m_pVolumeGeometry->getPixelLengthY() * RxOverRy * inv_pixelLengthX;
+ S = 0.5f - 0.5f*fabs(RxOverRy);
+ T = 0.5f + 0.5f*fabs(RxOverRy);
+ invTminS = 1.0f / (T-S);
} else {
+ RyOverRx = proj->fRayY/proj->fRayX;
+ deltar = -m_pVolumeGeometry->getPixelLengthX() * RyOverRx * inv_pixelLengthY;
+ S = 0.5f - 0.5f*fabs(RyOverRx);
+ T = 0.5f + 0.5f*fabs(RyOverRx);
+ invTminS = 1.0f / (T-S);
+ }
- // Precalculate kernel limits
- S = -0.5f * fabs_cos_theta * fabs_inv_sin_theta;
- T = -S;
- U = 1.0f + S;
- V = 1.0f - S;
- inv_4T = 0.25f / T;
-
- updateX = cos_theta * inv_sin_theta;
-
- // loop detectors
- for (iDetector = _iDetFrom; iDetector < _iDetTo; ++iDetector) {
-
- iRayIndex = iAngle * m_pProjectionGeometry->getDetectorCount() + iDetector;
-
- // POLICY: RAY PRIOR
- if (!p.rayPrior(iRayIndex)) continue;
+ Ex = m_pVolumeGeometry->getWindowMinY() + pixelLengthX*0.5f;
+ Ey = m_pVolumeGeometry->getWindowMaxY() - pixelLengthY*0.5f;
- // get t
- t = m_pProjectionGeometry->indexToDetectorOffset(iDetector);
- if (switch_t) t = -t;
+ // loop detectors
+ for (iDetector = _iDetFrom; iDetector < _iDetTo; ++iDetector) {
- // calculate left strip extremes (volume coordinates)
- PL = (t - cos_theta * m_pVolumeGeometry->pixelColToCenterX(0) + DW*0.5f) * inv_sin_theta;
- PLimitL = PL + 0.5f * fabs_cos_theta * fabs_inv_sin_theta * PW;
- PLimitR = PLimitL - DW * inv_sin_theta - PH * fabs_cos_theta * fabs_inv_sin_theta;
-
- // calculate strip extremes (pixel coordinates)
- XLimitL = (m_pVolumeGeometry->getWindowMaxY() - PLimitL) * inv_PH;
- XLimitR = (m_pVolumeGeometry->getWindowMaxY() - PLimitR) * inv_PH;
- xL = (m_pVolumeGeometry->getWindowMaxY() - PL) * inv_PH;
- xR = xL + (DW * fabs_inv_sin_theta) * 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;
- XLimitR += updateX;
- xL += updateX;
- xR += updateX;
+ iRayIndex = iAngle * detCount + iDetector;
- // for each affected col
- 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) res = 1.0f;
- else if (x2R > U) res = x2R - (x2R-U)*(x2R-U)*inv_4T;
- else if (x2R >= T) res = x2R;
- else if (x2R > S) res = (x2R-S)*(x2R-S) * inv_4T;
- else { x2L -= 1.0f; x2R -= 1.0f; continue; }
-
- // left
- if (x2L <= S) {} // - 0.0f
- else if (x2L < T) res -= (x2L-S)*(x2L-S) * inv_4T;
- else if (x2L <= U) res -= x2L;
- else if (x2L < V) res -= x2L - (x2L-U)*(x2L-U)*inv_4T;
- else { x2L -= 1.0f; x2R -= 1.0f; continue; }
-
- // POLICY: ADD
- p.addWeight(iRayIndex, iVolumeIndex, PW*PH * res);
-
- // POLICY: PIXEL POSTERIOR
- p.pixelPosterior(iVolumeIndex);
-
- x2L -= 1.0f;
- x2R -= 1.0f;
-
- } // end row loop
-
- } // end col loop
-
- // POLICY: RAY POSTERIOR
- p.rayPosterior(iRayIndex);
-
- } // end detector loop
-
-
- } // end theta switch
+ // POLICY: RAY PRIOR
+ if (!p.rayPrior(iRayIndex)) continue;
+
+ DLx = proj->fDetSX + iDetector * proj->fDetUX;
+ DLy = proj->fDetSY + iDetector * proj->fDetUY;
+ DRx = DLx + proj->fDetUX;
+ DRy = DLy + proj->fDetUY;
+
+ // vertically
+ if (vertical) {
+
+ // calculate cL and cR for row 0
+ cL = (DLx + (Ey - DLy)*RxOverRy - Ex) * inv_pixelLengthX;
+ cR = (DRx + (Ey - DRy)*RxOverRy - Ex) * inv_pixelLengthX;
+
+ if (cR < cL) {
+ float32 tmp = cL;
+ cL = cR;
+ cR = tmp;
+ }
+
+ // loop rows
+ for (row = 0; row < rowCount; ++row, cL += deltac, cR += deltac) {
+
+ col_left = int(cL-0.5f+S);
+ col_right = int(cR+1.5-S);
+
+ if (col_left < 0) col_left = 0;
+ if (col_right > colCount-1) col_right = colCount-1;
+
+ float32 tmp = float32(col_left);
+ offsetL = cL - tmp;
+ offsetR = cR - tmp;
+
+ // loop columns
+ for (col = col_left; col <= col_right; ++col, offsetL -= 1.0f, offsetR -= 1.0f) {
+
+ iVolumeIndex = row * colCount + col;
+ // POLICY: PIXEL PRIOR + ADD + POSTERIOR
+ if (p.pixelPrior(iVolumeIndex)) {
+
+ // right ray edge
+ if (T <= offsetR) res = 1.0f;
+ else if (S < offsetR) res = 1.0f - 0.5f*(T-offsetR)*(T-offsetR)*invTminS;
+ else if (-S < offsetR) res = 0.5f + offsetR;
+ else if (-T < offsetR) res = 0.5f*(offsetR+T)*(offsetR+T)*invTminS;
+ else res = 0.0f;
+
+ // left ray edge
+ if (T <= offsetL) res -= 1.0f;
+ else if (S < offsetL) res -= 1.0f - 0.5f*(T-offsetL)*(T-offsetL)*invTminS;
+ else if (-S < offsetL) res -= 0.5f + offsetL;
+ else if (-T < offsetL) res -= 0.5f*(offsetL+T)*(offsetL+T)*invTminS;
+
+ p.addWeight(iRayIndex, iVolumeIndex, pixelArea*res);
+ p.pixelPosterior(iVolumeIndex);
+ }
+ }
+ }
+ }
+
+ // horizontally
+ else {
+
+ // calculate rL and rR for row 0
+ rL = -(DLy + (Ex - DLx)*RyOverRx - Ey) * inv_pixelLengthY;
+ rR = -(DRy + (Ex - DRx)*RyOverRx - Ey) * inv_pixelLengthY;
+
+ if (rR < rL) {
+ float32 tmp = rL;
+ rL = rR;
+ rR = tmp;
+ }
+
+ // loop columns
+ for (col = 0; col < colCount; ++col, rL += deltar, rR += deltar) {
+
+ row_top = int(rL-0.5f+S);
+ row_bottom = int(rR+1.5-S);
+
+ if (row_top < 0) row_top = 0;
+ if (row_bottom > rowCount-1) row_bottom = rowCount-1;
+
+ float32 tmp = float32(row_top);
+ offsetL = rL - tmp;
+ offsetR = rR - tmp;
+
+ // loop rows
+ for (row = row_top; row <= row_bottom; ++row, offsetL -= 1.0f, offsetR -= 1.0f) {
+
+ iVolumeIndex = row * colCount + col;
+ // POLICY: PIXEL PRIOR + ADD + POSTERIOR
+ if (p.pixelPrior(iVolumeIndex)) {
+
+ // right ray edge
+ if (T <= offsetR) res = 1.0f;
+ else if (S < offsetR) res = 1.0f - 0.5f*(T-offsetR)*(T-offsetR)*invTminS;
+ else if (-S < offsetR) res = 0.5f + offsetR;
+ else if (-T < offsetR) res = 0.5f*(offsetR+T)*(offsetR+T)*invTminS;
+ else res = 0.0f;
+
+ // left ray edge
+ if (T <= offsetL) res -= 1.0f;
+ else if (S < offsetL) res -= 1.0f - 0.5f*(T-offsetL)*(T-offsetL)*invTminS;
+ else if (-S < offsetL) res -= 0.5f + offsetL;
+ else if (-T < offsetL) res -= 0.5f*(offsetL+T)*(offsetL+T)*invTminS;
+
+ p.addWeight(iRayIndex, iVolumeIndex, pixelArea*res);
+ p.pixelPosterior(iVolumeIndex);
+ }
+ }
+ }
+ }
+
+ // POLICY: RAY POSTERIOR
+ p.rayPosterior(iRayIndex);
+
+ } // end loop detector
+ } // end loop angles
- } // end angle loop
}
-
-
diff --git a/include/astra/ParallelProjectionGeometry2D.h b/include/astra/ParallelProjectionGeometry2D.h
index 2f7d36f..36b4b6f 100644
--- a/include/astra/ParallelProjectionGeometry2D.h
+++ b/include/astra/ParallelProjectionGeometry2D.h
@@ -30,6 +30,7 @@ $Id$
#define _INC_ASTRA_PARALLELPROJECTIONGEOMETRY2D
#include "ProjectionGeometry2D.h"
+#include "ParallelVecProjectionGeometry2D.h"
namespace astra
{
@@ -134,7 +135,7 @@ public:
* @param _sType geometry type to compare to.
* @return true if _sType == "parallel".
*/
- virtual bool isOfType(const std::string& _sType);
+ virtual bool isOfType(const std::string& _sType);
/** Get all settings in a Config object.
*
@@ -151,7 +152,12 @@ public:
*
* @return a unit vector describing the direction
*/
- virtual CVector3D getProjectionDirection(int _iProjectionIndex, int _iDetectorIndex = 0);
+ virtual CVector3D getProjectionDirection(int _iProjectionIndex, int _iDetectorIndex = 0);
+
+ /** Create a vector geom
+ */
+ CParallelVecProjectionGeometry2D* toVectorGeometry();
+
};
} // namespace astra
diff --git a/include/astra/ParallelVecProjectionGeometry2D.h b/include/astra/ParallelVecProjectionGeometry2D.h
new file mode 100644
index 0000000..96f8a54
--- /dev/null
+++ b/include/astra/ParallelVecProjectionGeometry2D.h
@@ -0,0 +1,163 @@
+/*
+-----------------------------------------------------------------------
+Copyright: 2010-2015, iMinds-Vision Lab, University of Antwerp
+ 2014-2015, CWI, Amsterdam
+
+Contact: astra@uantwerpen.be
+Website: http://sf.net/projects/astra-toolbox
+
+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 <http://www.gnu.org/licenses/>.
+
+-----------------------------------------------------------------------
+$Id$
+*/
+
+#ifndef _INC_ASTRA_PARALLELVECPROJECTIONGEOMETRY2D
+#define _INC_ASTRA_PARALLELVECPROJECTIONGEOMETRY2D
+
+#include "ProjectionGeometry2D.h"
+#include "GeometryUtil2D.h"
+
+namespace astra
+{
+
+/**
+ * This class defines a 2D parallel beam projection geometry.
+ *
+ * \par XML Configuration
+ * \astra_xml_item{DetectorCount, int, Number of detectors for each projection.}
+ *
+ * \par MATLAB example
+ * \astra_code{
+ * proj_geom = astra_struct('parallel_vec');\n
+ * proj_geom.DetectorCount = 512;\n
+ * proj_geom.Vectors = V;\n
+ * }
+ *
+ * \par Vectors
+ * Vectors is a matrix containing the actual geometry. Each row corresponds
+ * to a single projection, and consists of:
+ * ( rayX, rayY, dX, dY, uX, uY)
+ * ray: the ray direction
+ * d : the centre of the detector line
+ * u : the vector from detector pixel (0) to (1)
+ */
+class _AstraExport CParallelVecProjectionGeometry2D : public CProjectionGeometry2D
+{
+protected:
+
+ SParProjection *m_pProjectionAngles;
+
+public:
+
+ /** Default constructor. Sets all variables to zero. Note that this constructor leaves the object in an unusable state and must
+ * be followed by a call to init().
+ */
+ CParallelVecProjectionGeometry2D();
+
+ /** Constructor.
+ *
+ * @param _iProjectionAngleCount Number of projection angles.
+ * @param _iDetectorCount Number of detectors, i.e., the number of detector measurements for each projection angle.
+ * @param _pfProjectionAngles Pointer to an array of projection angles. The angles will be copied from this array.
+ */
+ CParallelVecProjectionGeometry2D(int _iProjectionAngleCount,
+ int _iDetectorCount,
+ const SParProjection* _pfProjectionAngles);
+
+ /** Copy constructor.
+ */
+ CParallelVecProjectionGeometry2D(const CParallelVecProjectionGeometry2D& _projGeom);
+
+ /** Assignment operator.
+ */
+ CParallelVecProjectionGeometry2D& operator=(const CParallelVecProjectionGeometry2D& _other);
+
+ /** Destructor.
+ */
+ virtual ~CParallelVecProjectionGeometry2D();
+
+ /** Initialize the geometry with a config object.
+ *
+ * @param _cfg Configuration Object
+ * @return initialization successful?
+ */
+ virtual bool initialize(const Config& _cfg);
+
+ /** Initialization. This function MUST be called after using the default constructor and MAY be called to
+ * reset a previously initialized object.
+ *
+ * @param _iProjectionAngleCount Number of projection angles.
+ * @param _iDetectorCount Number of detectors, i.e., the number of detector measurements for each projection angle.
+ * @param _pfProjectionAngles Pointer to an array of projection angles. The angles will be copied from this array.
+ */
+ bool initialize(int _iProjectionAngleCount,
+ int _iDetectorCount,
+ const SParProjection* _pfProjectionAngles);
+
+ virtual bool _check();
+
+ /** Create a hard copy.
+ */
+ virtual CProjectionGeometry2D* clone();
+
+ /** Returns true if the type of geometry defined in this class is the one specified in _sType.
+ *
+ * @param _sType geometry type to compare to.
+ * @return true if _sType == "fanflat_vec".
+ */
+ virtual bool isOfType(const std::string& _sType);
+
+ /** Return true if this geometry instance is the same as the one specified.
+ *
+ * @return true if this geometry instance is the same as the one specified.
+ */
+ virtual bool isEqual(CProjectionGeometry2D*) const;
+
+ /** Get all settings in a Config object.
+ *
+ * @return Configuration Object.
+ */
+ virtual Config* getConfiguration() const;
+
+
+ /** Get the value for t and theta, based upon the row and column index.
+ *
+ * @param _iRow row index
+ * @param _iColumn column index
+ * @param _fT output: value of t
+ * @param _fTheta output: value of theta, always lies within the [0,pi[ interval.
+ */
+ virtual void getRayParams(int _iRow, int _iColumn, float32& _fT, float32& _fTheta) const;
+
+ /**
+ * Returns a vector describing the direction of a ray belonging to a certain detector
+ *
+ * @param _iProjectionIndex index of projection
+ * @param _iProjectionIndex index of detector
+ *
+ * @return a unit vector describing the direction
+ */
+ virtual CVector3D getProjectionDirection(int _iProjectionIndex, int _iDetectorIndex);
+
+ const SParProjection* getProjectionVectors() const { return m_pProjectionAngles; }
+
+};
+
+} // namespace astra
+
+#endif /* _INC_ASTRA_PARALLELVECPROJECTIONGEOMETRY2D */
diff --git a/include/astra/ProjectionGeometry2D.h b/include/astra/ProjectionGeometry2D.h
index b8324e2..d26e6a7 100644
--- a/include/astra/ProjectionGeometry2D.h
+++ b/include/astra/ProjectionGeometry2D.h
@@ -65,10 +65,6 @@ protected:
*/
float32 m_fDetectorWidth;
- /** An array of m_iProjectionAngleCount elements containing an extra detector offset for each projection.
- */
- float32* m_pfExtraDetectorOffset;
-
/** Dynamically allocated array of projection angles. All angles are represented in radians and lie in
* the [0,2pi[ interval.
*/
@@ -202,9 +198,6 @@ public:
*/
float32 getProjectionAngleDegrees(int _iProjectionIndex) const;
- float32 getExtraDetectorOffset(int iAngle) const;
- const float32* getExtraDetectorOffset() const { return m_pfExtraDetectorOffset; }
-
/** Get the index coordinate of a point on a detector array.
*
* @param _fOffset distance between the center of the detector array and a certain point
@@ -273,12 +266,6 @@ public:
//----------------------------------------------------------------------------------------
-inline float32 CProjectionGeometry2D::getExtraDetectorOffset(int _iAngle) const
-{
- return m_pfExtraDetectorOffset ? m_pfExtraDetectorOffset[_iAngle] : 0.0f;
-}
-
-
// Get the initialization state.
inline bool CProjectionGeometry2D::isInitialized() const
{
diff --git a/matlab/algorithms/DART/IterativeTomography.m b/matlab/algorithms/DART/IterativeTomography.m
index 3875e6b..c3e737f 100644
--- a/matlab/algorithms/DART/IterativeTomography.m
+++ b/matlab/algorithms/DART/IterativeTomography.m
@@ -77,7 +77,7 @@ classdef IterativeTomography < matlab.mixin.Copyable
function ok = initialize(this)
% Initialize this object. Returns 1 if succesful.
% >> tomography.initialize();
-
+ disp('sdfqnlmkqdsfmlkjdfqsjklm');
% create projection geometry with super-resolution
if this.superresolution > 1
this.proj_geom_sr = astra_geom_superresolution(this.proj_geom, this.superresolution);
diff --git a/matlab/algorithms/DART/examples/example1.m b/matlab/algorithms/DART/examples/example1.m
index cb02e0f..791e440 100644
--- a/matlab/algorithms/DART/examples/example1.m
+++ b/matlab/algorithms/DART/examples/example1.m
@@ -35,36 +35,41 @@ vol_geom = astra_create_vol_geom(det_count, det_count);
[sinogram_id, sinogram] = astra_create_sino_cuda(I, proj_geom, vol_geom);
astra_mex_data2d('delete', sinogram_id);
-% DART
-D = DARTalgorithm(sinogram, proj_geom);
-D.t0 = 100;
-D.t = 10;
+ % DART
+ D = DARTalgorithm(sinogram, proj_geom);
+ D.t0 = 100;
+ D.t = 10;
-D.tomography.method = 'SIRT_CUDA';
-D.tomography.gpu_core = gpu_core;
-D.tomography.use_minc = 'yes';
+ D.tomography.method = 'SIRT';
+ D.tomography.gpu_core = gpu_core;
+ D.tomography.use_minc = 'yes';
+ D.tomography.gpu = 'no';
-D.segmentation.rho = rho;
-D.segmentation.tau = tau;
+ D.segmentation = SegmentationPDM();
+ D.segmentation.rho = rho*1.8;
+ D.segmentation.tau = tau*1.5;
+ D.segmentation.interval = 5;
-D.smoothing.b = 0.1;
-D.smoothing.gpu_core = gpu_core;
-
-D.masking.random = 0.1;
-D.masking.gpu_core = gpu_core;
+ D.smoothing.b = 0.1;
+ D.smoothing.gpu_core = gpu_core;
+ D.smoothing.gpu = 'no';
+
+ D.masking.random = 0.1;
+ D.masking.gpu_core = gpu_core;
+ D.masking.gpu = 'no';
+
+ D.output.directory = outdir;
+ D.output.pre = [prefix '_'];
+ D.output.save_images = 'no';
+ D.output.save_results = {'stats', 'settings', 'S', 'V'};
+ D.output.save_interval = dart_iterations;
+ D.output.verbose = 'yes';
-D.output.directory = outdir;
-D.output.pre = [prefix '_'];
-D.output.save_images = 'no';
-D.output.save_results = {'stats', 'settings', 'S', 'V'};
-D.output.save_interval = dart_iterations;
-D.output.verbose = 'yes';
+ D.statistics.proj_diff = 'no';
-D.statistics.proj_diff = 'no';
+ D.initialize();
-D.initialize();
-
-D.iterate(dart_iterations);
+ D.iterate(dart_iterations);
% save the reconstruction and the segmentation to file
imwritesc(D.S, [outdir '/' prefix '_S.png']);
diff --git a/matlab/mex/astra_mex_data2d_c.cpp b/matlab/mex/astra_mex_data2d_c.cpp
index 935e476..6371c9e 100644
--- a/matlab/mex/astra_mex_data2d_c.cpp
+++ b/matlab/mex/astra_mex_data2d_c.cpp
@@ -45,6 +45,7 @@ $Id$
#include "astra/SparseMatrixProjectionGeometry2D.h"
#include "astra/FanFlatProjectionGeometry2D.h"
#include "astra/FanFlatVecProjectionGeometry2D.h"
+#include "astra/ParallelVecProjectionGeometry2D.h"
using namespace std;
using namespace astra;
@@ -160,6 +161,8 @@ void astra_mex_data2d_create(int& nlhs, mxArray* plhs[], int& nrhs, const mxArra
pGeometry = new CFanFlatProjectionGeometry2D();
} else if (type == "fanflat_vec") {
pGeometry = new CFanFlatVecProjectionGeometry2D();
+ } else if (type == "parallel_vec") {
+ pGeometry = new CParallelVecProjectionGeometry2D();
} else {
pGeometry = new CParallelProjectionGeometry2D();
}
@@ -449,6 +452,8 @@ void astra_mex_data2d_change_geometry(int nlhs, mxArray* plhs[], int nrhs, const
pGeometry = new CFanFlatProjectionGeometry2D();
} else if (type == "fanflat_vec") {
pGeometry = new CFanFlatVecProjectionGeometry2D();
+ } else if (type == "parallel_vec") {
+ pGeometry = new CParallelVecProjectionGeometry2D();
} else {
pGeometry = new CParallelProjectionGeometry2D();
}
diff --git a/matlab/tools/astra_create_proj_geom.m b/matlab/tools/astra_create_proj_geom.m
index 862e410..ff7d74d 100644
--- a/matlab/tools/astra_create_proj_geom.m
+++ b/matlab/tools/astra_create_proj_geom.m
@@ -107,6 +107,19 @@ if strcmp(type,'parallel')
'ProjectionAngles', varargin{3} ...
);
+elseif strcmp(type,'parallel_vec')
+ if numel(varargin) < 2
+ error('not enough variables: astra_create_proj_geom(parallel_vec, det_count, V')
+ end
+ if size(varargin{2}, 2) ~= 6
+ error('V should be a Nx6 matrix, with N the number of projections')
+ end
+ proj_geom = struct( ...
+ 'type', 'parallel_vec', ...
+ 'DetectorCount', varargin{1}, ...
+ 'Vectors', varargin{2} ...
+ );
+
elseif strcmp(type,'fanflat')
if numel(varargin) < 5
error('not enough variables: astra_create_proj_geom(fanflat, det_width, det_count, angles, source_origin, source_det)');
diff --git a/matlab/tools/astra_geom_2vec.m b/matlab/tools/astra_geom_2vec.m
index 0abd07c..e563f47 100644
--- a/matlab/tools/astra_geom_2vec.m
+++ b/matlab/tools/astra_geom_2vec.m
@@ -1,25 +1,65 @@
-function proj_geom_out = astra_geom_2vec(proj_geom)
+function proj_geom_vec = astra_geom_2vec(proj_geom)
+%--------------------------------------------------------------------------
+% proj_geom_vec = astra_geom_2vec(proj_geom)
+%
+% Convert a conventional projection geometry to a corresponding vector-base
+% projection geometry
+%
+% proj_geom: input projection geometry (parallel, fanflat, parallel3d, cone)
+% proj_geom_vec: output vector-base projection geometry
+%--------------------------------------------------------------------------
+% This file is part of the ASTRA Toolbox
+%
+% Copyright: 2010-2015, iMinds-Vision Lab, University of Antwerp
+% 2014-2015, CWI, Amsterdam
+% License: Open Source under GPLv3
+% Contact: astra@uantwerpen.be
+% Website: http://sf.net/projects/astra-toolbox
+%--------------------------------------------------------------------------
+% $Id$
+
+ % PARALLEL 2D
+ if strcmp(proj_geom.type,'parallel')
+
+ vectors = zeros(numel(proj_geom.ProjectionAngles), 6);
+ for i = 1:numel(proj_geom.ProjectionAngles)
+
+ % ray direction
+ vectors(i,1) = sin(proj_geom.ProjectionAngles(i));
+ vectors(i,2) = -cos(proj_geom.ProjectionAngles(i));
+
+ % center of detector
+ vectors(i,3) = 0;
+ vectors(i,4) = 0;
+
+ % vector from detector pixel 0 to 1
+ vectors(i,5) = cos(proj_geom.ProjectionAngles(i)) * proj_geom.DetectorWidth;
+ vectors(i,6) = sin(proj_geom.ProjectionAngles(i)) * proj_geom.DetectorWidth;
+
+ end
+
+ proj_geom_vec = astra_create_proj_geom('parallel_vec', proj_geom.DetectorCount, vectors);
% FANFLAT
- if strcmp(proj_geom.type,'fanflat')
+ elseif strcmp(proj_geom.type,'fanflat')
vectors = zeros(numel(proj_geom.ProjectionAngles), 6);
for i = 1:numel(proj_geom.ProjectionAngles)
% source
vectors(i,1) = sin(proj_geom.ProjectionAngles(i)) * proj_geom.DistanceOriginSource;
- vectors(i,2) = -cos(proj_geom.ProjectionAngles(i)) * proj_geom.DistanceOriginSource;
+ vectors(i,2) = -cos(proj_geom.ProjectionAngles(i)) * proj_geom.DistanceOriginSource;
% center of detector
vectors(i,3) = -sin(proj_geom.ProjectionAngles(i)) * proj_geom.DistanceOriginDetector;
- vectors(i,4) = cos(proj_geom.ProjectionAngles(i)) * proj_geom.DistanceOriginDetector;
+ vectors(i,4) = cos(proj_geom.ProjectionAngles(i)) * proj_geom.DistanceOriginDetector;
% vector from detector pixel 0 to 1
vectors(i,5) = cos(proj_geom.ProjectionAngles(i)) * proj_geom.DetectorWidth;
vectors(i,6) = sin(proj_geom.ProjectionAngles(i)) * proj_geom.DetectorWidth;
end
- proj_geom_out = astra_create_proj_geom('fanflat_vec', proj_geom.DetectorCount, vectors);
+ proj_geom_vec = astra_create_proj_geom('fanflat_vec', proj_geom.DetectorCount, vectors);
% CONE
elseif strcmp(proj_geom.type,'cone')
@@ -29,13 +69,13 @@ function proj_geom_out = astra_geom_2vec(proj_geom)
% source
vectors(i,1) = sin(proj_geom.ProjectionAngles(i)) * proj_geom.DistanceOriginSource;
- vectors(i,2) = -cos(proj_geom.ProjectionAngles(i)) * proj_geom.DistanceOriginSource;
- vectors(i,3) = 0;
+ vectors(i,2) = -cos(proj_geom.ProjectionAngles(i)) * proj_geom.DistanceOriginSource;
+ vectors(i,3) = 0;
% center of detector
vectors(i,4) = -sin(proj_geom.ProjectionAngles(i)) * proj_geom.DistanceOriginDetector;
- vectors(i,5) = cos(proj_geom.ProjectionAngles(i)) * proj_geom.DistanceOriginDetector;
- vectors(i,6) = 0;
+ vectors(i,5) = cos(proj_geom.ProjectionAngles(i)) * proj_geom.DistanceOriginDetector;
+ vectors(i,6) = 0;
% vector from detector pixel (0,0) to (0,1)
vectors(i,7) = cos(proj_geom.ProjectionAngles(i)) * proj_geom.DetectorSpacingX;
@@ -45,13 +85,13 @@ function proj_geom_out = astra_geom_2vec(proj_geom)
% vector from detector pixel (0,0) to (1,0)
vectors(i,10) = 0;
vectors(i,11) = 0;
- vectors(i,12) = proj_geom.DetectorSpacingY;
+ vectors(i,12) = proj_geom.DetectorSpacingY;
end
- proj_geom_out = astra_create_proj_geom('cone_vec', proj_geom.DetectorRowCount, proj_geom.DetectorColCount, vectors);
+ proj_geom_vec = astra_create_proj_geom('cone_vec', proj_geom.DetectorRowCount, proj_geom.DetectorColCount, vectors);
- % PARALLEL
- elseif strcmp(proj_geom.type,'parallel3d')
+ % PARALLEL 3D
+ elseif strcmp(proj_geom.type,'parallel3d')
vectors = zeros(numel(proj_geom.ProjectionAngles), 12);
for i = 1:numel(proj_geom.ProjectionAngles)
@@ -59,12 +99,12 @@ function proj_geom_out = astra_geom_2vec(proj_geom)
% ray direction
vectors(i,1) = sin(proj_geom.ProjectionAngles(i));
vectors(i,2) = -cos(proj_geom.ProjectionAngles(i));
- vectors(i,3) = 0;
+ vectors(i,3) = 0;
% center of detector
vectors(i,4) = 0;
vectors(i,5) = 0;
- vectors(i,6) = 0;
+ vectors(i,6) = 0;
% vector from detector pixel (0,0) to (0,1)
vectors(i,7) = cos(proj_geom.ProjectionAngles(i)) * proj_geom.DetectorSpacingX;
@@ -74,11 +114,13 @@ function proj_geom_out = astra_geom_2vec(proj_geom)
% vector from detector pixel (0,0) to (1,0)
vectors(i,10) = 0;
vectors(i,11) = 0;
- vectors(i,12) = proj_geom.DetectorSpacingY;
+ vectors(i,12) = proj_geom.DetectorSpacingY;
end
- proj_geom_out = astra_create_proj_geom('parallel3d_vec', proj_geom.DetectorRowCount, proj_geom.DetectorColCount, vectors);
+ proj_geom_vec = astra_create_proj_geom('parallel3d_vec', proj_geom.DetectorRowCount, proj_geom.DetectorColCount, vectors);
else
error(['No suitable vector geometry found for type: ' proj_geom.type])
end
+
+end
diff --git a/matlab/tools/astra_geom_postalignment.m b/matlab/tools/astra_geom_postalignment.m
index 4115af2..c16f8ed 100644
--- a/matlab/tools/astra_geom_postalignment.m
+++ b/matlab/tools/astra_geom_postalignment.m
@@ -1,11 +1,36 @@
function proj_geom = astra_geom_postalignment(proj_geom, factor)
+%--------------------------------------------------------------------------
+% proj_geom = astra_geom_postalignment(proj_geom, factorU)
+% proj_geom = astra_geom_postalignment(proj_geom, [factorU factorV])
+%
+% Apply a postalignment to a vector-based projection geometry. Can be used to model the rotation axis offset.
+%
+% proj_geom: input projection geometry (vector-based only, use astra_geom_2vec to convert conventional projection geometries)
+% dim (optional): which dimension
+% s: output
+%--------------------------------------------------------------------------
+% This file is part of the ASTRA Toolbox
+%
+% Copyright: 2010-2015, iMinds-Vision Lab, University of Antwerp
+% 2014-2015, CWI, Amsterdam
+% License: Open Source under GPLv3
+% Contact: astra@uantwerpen.be
+% Website: http://sf.net/projects/astra-toolbox
+%--------------------------------------------------------------------------
+% $Id$
+
+ if strcmp(proj_geom.type,'fanflat_vec') || strcmp(proj_geom.type,'parallel_vec')
+ proj_geom.Vectors(:,3:4) = proj_geom.Vectors(:,3:4) + factor(1) * proj_geom.Vectors(:,5:6);
- if strcmp(proj_geom.type,'fanflat_vec')
- proj_geom.Vectors(:,3:4) = proj_geom.Vectors(:,3:4) + factor * proj_geom.Vectors(:,5:6);
-
elseif strcmp(proj_geom.type,'cone_vec') || strcmp(proj_geom.type,'parallel3d_vec')
- proj_geom.Vectors(:,4:6) = proj_geom.Vectors(:,4:6) + factor * proj_geom.Vectors(:,7:9);
+ if numel(factor) == 1
+ proj_geom.Vectors(:,4:6) = proj_geom.Vectors(:,4:6) + factor * proj_geom.Vectors(:,7:9);
+ elseif numel(factor) > 1
+ proj_geom.Vectors(:,4:6) = proj_geom.Vectors(:,4:6) + factor(1) * proj_geom.Vectors(:,7:9) + factor(2) * proj_geom.Vectors(:,10:12);
+ end
else
error('Projection geometry not suited for postalignment correction.')
end
+
+end \ No newline at end of file
diff --git a/matlab/tools/astra_geom_size.m b/matlab/tools/astra_geom_size.m
index 7044515..b3c1522 100644
--- a/matlab/tools/astra_geom_size.m
+++ b/matlab/tools/astra_geom_size.m
@@ -1,4 +1,22 @@
function s = astra_geom_size(geom, dim)
+%--------------------------------------------------------------------------
+% s = astra_geom_size(geom, dim)
+%
+% Get the size of a volume or projection geometry.
+%
+% geom: volume or projection geometry
+% dim (optional): which dimension
+% s: output
+%--------------------------------------------------------------------------
+% This file is part of the ASTRA Toolbox
+%
+% Copyright: 2010-2015, iMinds-Vision Lab, University of Antwerp
+% 2014-2015, CWI, Amsterdam
+% License: Open Source under GPLv3
+% Contact: astra@uantwerpen.be
+% Website: http://sf.net/projects/astra-toolbox
+%--------------------------------------------------------------------------
+% $Id$
if isfield(geom, 'GridSliceCount')
% 3D Volume geometry?
@@ -6,23 +24,23 @@ function s = astra_geom_size(geom, dim)
elseif isfield(geom, 'GridColCount')
% 2D Volume geometry?
s = [ geom.GridRowCount, geom.GridColCount ];
- elseif strcmp(geom.type,'parallel') || strcmp(geom.type,'fanflat')
+ elseif strcmp(geom.type,'parallel') || strcmp(geom.type,'fanflat')
s = [numel(geom.ProjectionAngles), geom.DetectorCount];
-
- elseif strcmp(geom.type,'parallel3d') || strcmp(geom.type,'cone')
+
+ elseif strcmp(geom.type,'parallel3d') || strcmp(geom.type,'cone')
s = [geom.DetectorColCount, numel(geom.ProjectionAngles), geom.DetectorRowCount];
-
- elseif strcmp(geom.type,'fanflat_vec')
+
+ elseif strcmp(geom.type,'fanflat_vec') || strcmp(geom.type,'parallel_vec')
s = [size(geom.Vectors,1), geom.DetectorCount];
-
- elseif strcmp(geom.type,'parallel3d_vec') || strcmp(geom.type,'cone_vec')
+
+ elseif strcmp(geom.type,'parallel3d_vec') || strcmp(geom.type,'cone_vec')
s = [geom.DetectorColCount, size(geom.Vectors,1), geom.DetectorRowCount];
-
+
end
if nargin == 2
s = s(dim);
end
-
+
end
diff --git a/matlab/tools/astra_geom_visualize.m b/matlab/tools/astra_geom_visualize.m
new file mode 100644
index 0000000..0044844
--- /dev/null
+++ b/matlab/tools/astra_geom_visualize.m
@@ -0,0 +1,216 @@
+function astra_geom_visualize(proj_geom, vol_geom)
+
+ if strcmp(proj_geom.type,'parallel') || strcmp(proj_geom.type,'fanflat') ||strcmp(proj_geom.type,'parallel3d') || strcmp(proj_geom.type,'cone')
+ proj_geom = astra_geom_2vec(proj_geom);
+ end
+
+ % open window
+ f = figure('Visible','off');
+ hold on
+
+ % display projection 1
+ displayProjection(1);
+
+ % label
+ txt = uicontrol('Style','text', 'Position',[10 10 70 20], 'String','Projection');
+
+ % slider
+ anglecount = size(proj_geom.Vectors,1);
+ sld = uicontrol('Style', 'slider', ...
+ 'Min', 1, 'Max', anglecount, 'SliderStep', [1 1]/anglecount, 'Value', 1, ...
+ 'Position', [80 10 200 20], ...
+ 'Callback', @updateProjection);
+
+ f.Visible = 'on';
+
+ function updateProjection(source, callbackdata)
+ displayProjection(floor(source.Value));
+ end
+
+ function displayProjection(a)
+
+ colours = get(gca,'ColorOrder');
+
+
+ % set title
+ title(['projection ' num2str(a)]);
+
+ if strcmp(proj_geom.type,'parallel_vec')
+
+ v = proj_geom.Vectors;
+ d = proj_geom.DetectorCount;
+
+ if ~isfield(vol_geom, 'option')
+ minx = -vol_geom.GridRowCount/2;
+ miny = -vol_geom.GridColCount/2;
+ minz = -vol_geom.GridSliceCount/2;
+ maxx = vol_geom.GridRowCount/2;
+ else
+ minx = vol_geom.option.WindowMinX;
+ miny = vol_geom.option.WindowMinY;
+ maxx = vol_geom.option.WindowMaxX;
+ maxy = vol_geom.option.WindowMaxY;
+ end
+
+ % axis
+ cla
+ axis([minx maxx miny maxy]*2.25)
+ axis square
+
+ % volume
+ plot([minx minx maxx maxx minx], [miny maxy maxy miny miny], 'LineWidth', 1, 'Color', colours(1,:))
+
+ % ray
+ s = maxx - minx;
+ plot([0 v(a,1)]*s*0.33, [0 v(a,2)]*s*0.33, 'LineWidth', 2, 'Color', colours(3,:))
+
+ % detector
+ s2 = s*0.75;
+ plot([-d/2 d/2]*v(a,5) + v(a,3) + s2*v(a,1), [-d/2 d/2]*v(a,6) + v(a,4) + s2*v(a,2), 'LineWidth', 2, 'Color', colours(5,:))
+
+ elseif strcmp(proj_geom.type,'fanflat_vec')
+
+ v = proj_geom.Vectors;
+ d = proj_geom.DetectorCount;
+
+ if ~isfield(vol_geom, 'option')
+ minx = -vol_geom.GridRowCount/2;
+ miny = -vol_geom.GridColCount/2;
+ minz = -vol_geom.GridSliceCount/2;
+ maxx = vol_geom.GridRowCount/2;
+ else
+ minx = vol_geom.option.WindowMinX;
+ miny = vol_geom.option.WindowMinY;
+ maxx = vol_geom.option.WindowMaxX;
+ maxy = vol_geom.option.WindowMaxY;
+ end
+
+ % axis
+ cla
+ axis([minx maxx miny maxy]*2.25)
+ axis square
+
+ % volume
+ plot([minx minx maxx maxx minx], [miny maxy maxy miny miny], 'LineWidth', 1, 'Color', colours(1,:))
+
+ % detector
+ D1 = v(a,3:4) - d/2*v(a,5:6);
+ D2 = v(a,3:4) + d/2*v(a,5:6);
+ plot([D1(1) D2(1)], [D1(2) D2(2)], 'LineWidth', 2, 'Color', colours(5,:))
+
+ % beam
+ plot([v(a,1) D1(1)], [v(a,2) D1(2)], 'LineWidth', 1, 'Color', colours(3,:))
+ plot([v(a,1) D2(1)], [v(a,2) D2(2)], 'LineWidth', 1, 'Color', colours(3,:))
+
+ elseif strcmp(proj_geom.type,'parallel3d_vec')
+
+ v = proj_geom.Vectors;
+ d1 = proj_geom.DetectorRowCount;
+ d2 = proj_geom.DetectorColCount;
+
+ if ~isfield(vol_geom, 'option')
+ minx = -vol_geom.GridRowCount/2;
+ miny = -vol_geom.GridColCount/2;
+ minz = -vol_geom.GridSliceCount/2;
+ maxx = vol_geom.GridRowCount/2;
+ maxy = vol_geom.GridColCount/2;
+ maxz = vol_geom.GridSliceCount/2;
+ else
+ minx = vol_geom.option.WindowMinX;
+ miny = vol_geom.option.WindowMinY;
+ minz = vol_geom.option.WindowMinZ;
+ maxx = vol_geom.option.WindowMaxX;
+ maxy = vol_geom.option.WindowMaxY;
+ maxz = vol_geom.option.WindowMaxZ;
+ end
+
+ % axis
+ windowminx = min(v(:,4));
+ windowminy = min(v(:,5));
+ windowminz = max(v(:,6));
+ windowmaxx = max(v(:,4));
+ windowmaxy = max(v(:,5));
+ windowmaxz = max(v(:,6));
+ cla
+ axis([minx maxx miny maxy minz maxz]*5.10)
+
+ % volume
+ plot3([minx minx maxx maxx minx], [miny maxy maxy miny miny], [minz minz minz minz minz], 'LineWidth', 1, 'Color', colours(1,:))
+ plot3([minx minx maxx maxx minx], [miny maxy maxy miny miny], [maxz maxz maxz maxz maxz], 'LineWidth', 1, 'Color', colours(1,:))
+ plot3([minx minx], [miny miny], [minz maxz], 'LineWidth', 1, 'Color', colours(1,:))
+ plot3([maxx maxx], [miny miny], [minz maxz], 'LineWidth', 1, 'Color', colours(1,:))
+ plot3([minx minx], [maxy maxy], [minz maxz], 'LineWidth', 1, 'Color', colours(1,:))
+ plot3([maxx maxx], [maxy maxy], [minz maxz], 'LineWidth', 1, 'Color', colours(1,:))
+
+ % detector
+ D1 = v(a,4:6) - d1/2*v(a,7:9) - d2/2*v(a,10:12);
+ D2 = v(a,4:6) + d1/2*v(a,7:9) - d2/2*v(a,10:12);
+ D3 = v(a,4:6) + d1/2*v(a,7:9) + d2/2*v(a,10:12);
+ D4 = v(a,4:6) - d1/2*v(a,7:9) + d2/2*v(a,10:12);
+ plot3([D1(1) D2(1) D3(1) D4(1) D1(1)], [D1(2) D2(2) D3(2) D4(2) D1(2)], [D1(3) D2(3) D3(3) D4(3) D1(3)], 'LineWidth', 2, 'Color', colours(5,:))
+
+ % ray
+ s = maxx - minx;
+ plot3([0 v(a,1)]*s*0.30, [0 v(a,2)]*s*0.30, [0 v(a,3)]*s*0.30, 'LineWidth', 2, 'Color', colours(3,:))
+
+ elseif strcmp(proj_geom.type,'cone_vec')
+
+ v = proj_geom.Vectors;
+ d1 = proj_geom.DetectorRowCount;
+ d2 = proj_geom.DetectorColCount;
+
+ if ~isfield(vol_geom, 'option')
+ minx = -vol_geom.GridRowCount/2;
+ miny = -vol_geom.GridColCount/2;
+ minz = -vol_geom.GridSliceCount/2;
+ maxx = vol_geom.GridRowCount/2;
+ maxy = vol_geom.GridColCount/2;
+ maxz = vol_geom.GridSliceCount/2;
+ else
+ minx = vol_geom.option.WindowMinX;
+ miny = vol_geom.option.WindowMinY;
+ minz = vol_geom.option.WindowMinZ;
+ maxx = vol_geom.option.WindowMaxX;
+ maxy = vol_geom.option.WindowMaxY;
+ maxz = vol_geom.option.WindowMaxZ;
+ end
+
+ % axis
+ windowminx = min(v(:,4));
+ windowminy = min(v(:,5));
+ windowminz = max(v(:,6));
+ windowmaxx = max(v(:,4));
+ windowmaxy = max(v(:,5));
+ windowmaxz = max(v(:,6));
+ cla
+ axis([minx maxx miny maxy minz maxz]*5.10)
+
+ % volume
+ plot3([minx minx maxx maxx minx], [miny maxy maxy miny miny], [minz minz minz minz minz], 'LineWidth', 1, 'Color', colours(1,:))
+ plot3([minx minx maxx maxx minx], [miny maxy maxy miny miny], [maxz maxz maxz maxz maxz], 'LineWidth', 1, 'Color', colours(1,:))
+ plot3([minx minx], [miny miny], [minz maxz], 'LineWidth', 1, 'Color', colours(1,:))
+ plot3([maxx maxx], [miny miny], [minz maxz], 'LineWidth', 1, 'Color', colours(1,:))
+ plot3([minx minx], [maxy maxy], [minz maxz], 'LineWidth', 1, 'Color', colours(1,:))
+ plot3([maxx maxx], [maxy maxy], [minz maxz], 'LineWidth', 1, 'Color', colours(1,:))
+
+ % detector
+ D1 = v(a,4:6) - d1/2*v(a,7:9) - d2/2*v(a,10:12);
+ D2 = v(a,4:6) + d1/2*v(a,7:9) - d2/2*v(a,10:12);
+ D3 = v(a,4:6) + d1/2*v(a,7:9) + d2/2*v(a,10:12);
+ D4 = v(a,4:6) - d1/2*v(a,7:9) + d2/2*v(a,10:12);
+ plot3([D1(1) D2(1) D3(1) D4(1) D1(1)], [D1(2) D2(2) D3(2) D4(2) D1(2)], [D1(3) D2(3) D3(3) D4(3) D1(3)], 'LineWidth', 2, 'Color', colours(5,:))
+
+ % beam
+ plot3([v(a,1) D1(1)], [v(a,2) D1(2)], [v(a,3) D1(3)], 'LineWidth', 1, 'Color', colours(3,:))
+ plot3([v(a,1) D2(1)], [v(a,2) D2(2)], [v(a,3) D2(3)], 'LineWidth', 1, 'Color', colours(3,:))
+ plot3([v(a,1) D3(1)], [v(a,2) D3(2)], [v(a,3) D3(3)], 'LineWidth', 1, 'Color', colours(3,:))
+ plot3([v(a,1) D4(1)], [v(a,2) D4(2)], [v(a,3) D4(3)], 'LineWidth', 1, 'Color', colours(3,:))
+
+
+ else
+ error('invalid projector type')
+
+ end
+ end
+
+end
diff --git a/python/astra/PyIncludes.pxd b/python/astra/PyIncludes.pxd
index 4a4ce43..61425c9 100644
--- a/python/astra/PyIncludes.pxd
+++ b/python/astra/PyIncludes.pxd
@@ -128,6 +128,10 @@ cdef extern from "astra/FanFlatVecProjectionGeometry2D.h" namespace "astra":
cdef cppclass CFanFlatVecProjectionGeometry2D:
CFanFlatVecProjectionGeometry2D()
+cdef extern from "astra/ParallelVecProjectionGeometry2D.h" namespace "astra":
+ cdef cppclass CParallelVecProjectionGeometry2D:
+ CParallelVecProjectionGeometry2D()
+
cdef extern from "astra/ParallelProjectionGeometry2D.h" namespace "astra":
cdef cppclass CParallelProjectionGeometry2D:
CParallelProjectionGeometry2D()
diff --git a/python/astra/creators.py b/python/astra/creators.py
index 18504ea..82fd9d1 100644
--- a/python/astra/creators.py
+++ b/python/astra/creators.py
@@ -155,6 +155,13 @@ This method can be called in a number of ways:
:type angles: :class:`numpy.ndarray`
:returns: A parallel projection geometry.
+``create_proj_geom('parallel_vec', det_count, V)``:
+
+:param det_count: Number of detector pixels.
+:type det_count: :class:`int`
+:param V: Vector array.
+:type V: :class:`numpy.ndarray`
+:returns: A parallel-beam projection geometry.
``create_proj_geom('fanflat', det_width, det_count, angles, source_origin, origin_det)``:
@@ -242,6 +249,12 @@ This method can be called in a number of ways:
raise Exception(
'not enough variables: astra_create_proj_geom(parallel, detector_spacing, det_count, angles)')
return {'type': 'parallel', 'DetectorWidth': args[0], 'DetectorCount': args[1], 'ProjectionAngles': args[2]}
+ elif intype == 'parallel_vec':
+ if len(args) < 2:
+ raise Exception('not enough variables: astra_create_proj_geom(parallel_vec, det_count, V)')
+ if not args[1].shape[1] == 6:
+ raise Exception('V should be a Nx6 matrix, with N the number of projections')
+ return {'type':'parallel_vec', 'DetectorCount':args[0], 'Vectors':args[1]}
elif intype == 'fanflat':
if len(args) < 5:
raise Exception('not enough variables: astra_create_proj_geom(fanflat, det_width, det_count, angles, source_origin, origin_det)')
diff --git a/python/astra/data2d_c.pyx b/python/astra/data2d_c.pyx
index 65e80ce..2242d3b 100644
--- a/python/astra/data2d_c.pyx
+++ b/python/astra/data2d_c.pyx
@@ -109,6 +109,8 @@ def create(datatype, geometry, data=None, link=False):
ppGeometry = <CProjectionGeometry2D * >new CFanFlatProjectionGeometry2D()
elif (tpe == 'fanflat_vec'):
ppGeometry = <CProjectionGeometry2D * >new CFanFlatVecProjectionGeometry2D()
+ elif (tpe == 'parallel_vec'):
+ ppGeometry = <CProjectionGeometry2D * >new CParallelVecProjectionGeometry2D()
else:
ppGeometry = <CProjectionGeometry2D * >new CParallelProjectionGeometry2D()
if not ppGeometry.initialize(cfg[0]):
@@ -222,6 +224,8 @@ def change_geometry(i, geom):
ppGeometry = <CProjectionGeometry2D * >new CFanFlatProjectionGeometry2D()
elif (tpe == 'fanflat_vec'):
ppGeometry = <CProjectionGeometry2D * >new CFanFlatVecProjectionGeometry2D()
+ elif (tpe == 'parallel_vec'):
+ ppGeometry = <CProjectionGeometry2D * >new CParallelVecProjectionGeometry2D()
else:
ppGeometry = <CProjectionGeometry2D * >new CParallelProjectionGeometry2D()
if not ppGeometry.initialize(cfg[0]):
diff --git a/python/astra/functions.py b/python/astra/functions.py
index 3f4aa82..9927eee 100644
--- a/python/astra/functions.py
+++ b/python/astra/functions.py
@@ -172,7 +172,27 @@ def geom_2vec(proj_geom):
:param proj_geom: Projection geometry to convert
:type proj_geom: :class:`dict`
"""
- if proj_geom['type'] == 'fanflat':
+
+ if proj_geom['type'] == 'parallel':
+ angles = proj_geom['ProjectionAngles']
+ vectors = np.zeros((len(angles), 6))
+ for i in range(len(angles)):
+
+ # source
+ vectors[i, 0] = np.sin(angles[i])
+ vectors[i, 1] = -np.cos(angles[i])
+
+ # center of detector
+ vectors[i, 2] = 0
+ vectors[i, 3] = 0
+
+ # vector from detector pixel 0 to 1
+ vectors[i, 4] = np.cos(angles[i]) * proj_geom['DetectorWidth']
+ vectors[i, 5] = np.sin(angles[i]) * proj_geom['DetectorWidth']
+ proj_geom_out = ac.create_proj_geom(
+ 'parallel_vec', proj_geom['DetectorCount'], vectors)
+
+ elif proj_geom['type'] == 'fanflat':
angles = proj_geom['ProjectionAngles']
vectors = np.zeros((len(angles), 6))
for i in range(len(angles)):
@@ -251,3 +271,37 @@ def geom_2vec(proj_geom):
raise ValueError(
'No suitable vector geometry found for type: ' + proj_geom['type'])
return proj_geom_out
+
+
+def geom_postalignment(proj_geom, factor):
+ """Returns the size of a volume or sinogram, based on the projection or volume geometry.
+
+ :param proj_geom: input projection geometry (vector-based only, use astra.geom_2vec to convert conventional projection geometries)
+ :type proj_geom: :class:`dict`
+ :param factor: Optional axis index to return
+ :type factor: :class:`float`
+ """
+
+ if proj_geom['type'] == 'parallel_vec' or proj_geom['type'] == 'fanflat_vec':
+ for i in range(proj_geom['Vectors'].shape[0]):
+ proj_geom['Vectors'][i,2] = proj_geom['Vectors'][i,2] + factor * proj_geom['Vectors'][i,4];
+ proj_geom['Vectors'][i,3] = proj_geom['Vectors'][i,3] + factor * proj_geom['Vectors'][i,5];
+
+ elif proj_geom['type'] == 'parallel3d_vec' or proj_geom['type'] == 'cone_vec':
+
+ if len(factor) == 1:
+ for i in range(proj_geom['Vectors'].shape[0]):
+ proj_geom['Vectors'][i,3] = proj_geom['Vectors'][i,3] + factor * proj_geom['Vectors'][i,6];
+ proj_geom['Vectors'][i,4] = proj_geom['Vectors'][i,4] + factor * proj_geom['Vectors'][i,7];
+ proj_geom['Vectors'][i,5] = proj_geom['Vectors'][i,5] + factor * proj_geom['Vectors'][i,8];
+
+ elif len(factor) > 1:
+ for i in range(proj_geom['Vectors'].shape[0]):
+ proj_geom['Vectors'][i,3] = proj_geom['Vectors'][i,3] + factor[0] * proj_geom['Vectors'][i,6] + factor[1] * proj_geom['Vectors'][i, 9];
+ proj_geom['Vectors'][i,4] = proj_geom['Vectors'][i,4] + factor[0] * proj_geom['Vectors'][i,7] + factor[1] * proj_geom['Vectors'][i,10];
+ proj_geom['Vectors'][i,5] = proj_geom['Vectors'][i,5] + factor[0] * proj_geom['Vectors'][i,8] + factor[1] * proj_geom['Vectors'][i,11];
+ else:
+ raise ValueError('No suitable geometry for postalignment: ' + proj_geom['type'])
+
+ return proj_geom
+
diff --git a/src/FanFlatProjectionGeometry2D.cpp b/src/FanFlatProjectionGeometry2D.cpp
index 8bee0d6..3aab582 100644
--- a/src/FanFlatProjectionGeometry2D.cpp
+++ b/src/FanFlatProjectionGeometry2D.cpp
@@ -214,7 +214,25 @@ Config* CFanFlatProjectionGeometry2D::getConfiguration() const
cfg->self.addChildNode("ProjectionAngles", m_pfProjectionAngles, m_iProjectionAngleCount);
return cfg;
}
+
//----------------------------------------------------------------------------------------
+CFanFlatVecProjectionGeometry2D* CFanFlatProjectionGeometry2D::toVectorGeometry()
+{
+ SFanProjection* vectors = new SFanProjection[m_iProjectionAngleCount];
+ for (int i = 0; i < m_iProjectionAngleCount; ++i)
+ {
+ vectors[i].fSrcX = sinf(m_pfProjectionAngles[i]) * m_fOriginSourceDistance;
+ vectors[i].fSrcY = -cosf(m_pfProjectionAngles[i]) * m_fOriginSourceDistance;
+ vectors[i].fDetUX = cosf(m_pfProjectionAngles[i]) * m_fDetectorWidth;
+ vectors[i].fDetUY = sinf(m_pfProjectionAngles[i]) * m_fDetectorWidth;
+ vectors[i].fDetSX = -sinf(m_pfProjectionAngles[i]) * m_fOriginDetectorDistance - 0.5f * m_iDetectorCount * vectors[i].fDetUX;
+ vectors[i].fDetSY = cosf(m_pfProjectionAngles[i]) * m_fOriginDetectorDistance - 0.5f * m_iDetectorCount * vectors[i].fDetUY;
+ }
+ CFanFlatVecProjectionGeometry2D* vecGeom = new CFanFlatVecProjectionGeometry2D();
+ vecGeom->initialize(m_iProjectionAngleCount, m_iDetectorCount, vectors);
+ delete[] vectors;
+ return vecGeom;
+}
} // namespace astra
diff --git a/src/ParallelBeamBlobKernelProjector2D.cpp b/src/ParallelBeamBlobKernelProjector2D.cpp
index 679d5c6..f6741dc 100644
--- a/src/ParallelBeamBlobKernelProjector2D.cpp
+++ b/src/ParallelBeamBlobKernelProjector2D.cpp
@@ -101,7 +101,7 @@ bool CParallelBeamBlobKernelProjector2D::_check()
// check base class
ASTRA_CONFIG_CHECK(CProjector2D::_check(), "ParallelBeamBlobKernelProjector2D", "Error in Projector2D initialization");
- ASTRA_CONFIG_CHECK(dynamic_cast<CParallelProjectionGeometry2D*>(m_pProjectionGeometry), "ParallelBeamBlobKernelProjector2D", "Unsupported projection geometry");
+ ASTRA_CONFIG_CHECK(dynamic_cast<CParallelProjectionGeometry2D*>(m_pProjectionGeometry) || dynamic_cast<CParallelVecProjectionGeometry2D*>(m_pProjectionGeometry), "ParallelBeamBlobKernelProjector2D", "Unsupported projection geometry");
ASTRA_CONFIG_CHECK(m_iBlobSampleCount > 0, "ParallelBeamBlobKernelProjector2D", "m_iBlobSampleCount should be strictly positive.");
ASTRA_CONFIG_CHECK(m_pfBlobValues, "ParallelBeamBlobKernelProjector2D", "Invalid Volume Geometry Object.");
@@ -154,17 +154,6 @@ bool CParallelBeamBlobKernelProjector2D::initialize(const Config& _cfg)
for (int i = 0; i < m_iBlobSampleCount; i++) {
m_pfBlobValues[i] = values[i];
}
-
- // Required: KernelValues
- node2 = node.getSingleNode("KernelValuesNeg");
- ASTRA_CONFIG_CHECK(node2, "BlobProjector", "No Kernel/KernelValuesNeg tag specified.");
- vector<float32> values2 = node2.getContentNumericalArray();
- ASTRA_CONFIG_CHECK(values2.size() == (unsigned int)m_iBlobSampleCount, "BlobProjector", "Number of specified values doesn't match SampleCount.");
- m_pfBlobValuesNeg = new float32[m_iBlobSampleCount];
- for (int i = 0; i < m_iBlobSampleCount; i++) {
- m_pfBlobValuesNeg[i] = values2[i];
- }
-
}
// success
@@ -227,44 +216,44 @@ void CParallelBeamBlobKernelProjector2D::computeSingleRayWeights(int _iProjectio
// Splat a single point
std::vector<SDetector2D> CParallelBeamBlobKernelProjector2D::projectPoint(int _iRow, int _iCol)
{
- float32 x = m_pVolumeGeometry->pixelColToCenterX(_iCol);
- float32 y = m_pVolumeGeometry->pixelRowToCenterY(_iRow);
-
- std::vector<SDetector2D> res;
- // loop projectors and detectors
- for (int iProjection = 0; iProjection < m_pProjectionGeometry->getProjectionAngleCount(); ++iProjection) {
-
- // get projection angle
- float32 theta = m_pProjectionGeometry->getProjectionAngle(iProjection);
- if (theta >= 7*PIdiv4) theta -= 2*PI;
- bool inverse = false;
- if (theta >= 3*PIdiv4) {
- theta -= PI;
- inverse = true;
- }
-
- // calculate distance from the center of the voxel to the ray though the origin
- float32 t = x * cos(theta) + y * sin(theta);
- if (inverse) t *= -1.0f;
-
- // calculate the offset on the detectorarray (in indices)
- float32 d = m_pProjectionGeometry->detectorOffsetToIndexFloat(t);
- int dmin = (int)ceil(d - m_fBlobSize);
- int dmax = (int)floor(d + m_fBlobSize);
-
- // add affected detectors to the list
- for (int i = dmin; i <= dmax; ++i) {
- if (d >= 0 && d < m_pProjectionGeometry->getDetectorCount()) {
- SDetector2D det;
- det.m_iAngleIndex = iProjection;
- det.m_iDetectorIndex = i;
- det.m_iIndex = iProjection * getProjectionGeometry()->getDetectorCount() + i;
- res.push_back(det);
- }
- }
- }
-
- // return result vector
- return res;
-
+ // float32 x = m_pVolumeGeometry->pixelColToCenterX(_iCol);
+ // float32 y = m_pVolumeGeometry->pixelRowToCenterY(_iRow);
+
+ // std::vector<SDetector2D> res;
+ // // loop projectors and detectors
+ // for (int iProjection = 0; iProjection < m_pProjectionGeometry->getProjectionAngleCount(); ++iProjection) {
+
+ // // get projection angle
+ // float32 theta = m_pProjectionGeometry->getProjectionAngle(iProjection);
+ // if (theta >= 7*PIdiv4) theta -= 2*PI;
+ // bool inverse = false;
+ // if (theta >= 3*PIdiv4) {
+ // theta -= PI;
+ // inverse = true;
+ // }
+
+ // // calculate distance from the center of the voxel to the ray though the origin
+ // float32 t = x * cos(theta) + y * sin(theta);
+ // if (inverse) t *= -1.0f;
+
+ // // calculate the offset on the detectorarray (in indices)
+ // float32 d = m_pProjectionGeometry->detectorOffsetToIndexFloat(t);
+ // int dmin = (int)ceil(d - m_fBlobSize);
+ // int dmax = (int)floor(d + m_fBlobSize);
+
+ // // add affected detectors to the list
+ // for (int i = dmin; i <= dmax; ++i) {
+ // if (d >= 0 && d < m_pProjectionGeometry->getDetectorCount()) {
+ // SDetector2D det;
+ // det.m_iAngleIndex = iProjection;
+ // det.m_iDetectorIndex = i;
+ // det.m_iIndex = iProjection * getProjectionGeometry()->getDetectorCount() + i;
+ // res.push_back(det);
+ // }
+ // }
+ // }
+
+ // // return result vector
+ // return res;
+ return std::vector<SDetector2D>();
}
diff --git a/src/ParallelBeamLineKernelProjector2D.cpp b/src/ParallelBeamLineKernelProjector2D.cpp
index e4a1bff..4b5a34f 100644
--- a/src/ParallelBeamLineKernelProjector2D.cpp
+++ b/src/ParallelBeamLineKernelProjector2D.cpp
@@ -87,7 +87,7 @@ bool CParallelBeamLineKernelProjector2D::_check()
// check base class
ASTRA_CONFIG_CHECK(CProjector2D::_check(), "ParallelBeamLineKernelProjector2D", "Error in Projector2D initialization");
- ASTRA_CONFIG_CHECK(dynamic_cast<CParallelProjectionGeometry2D*>(m_pProjectionGeometry), "ParallelBeamLineKernelProjector2D", "Unsupported projection geometry");
+ ASTRA_CONFIG_CHECK(dynamic_cast<CParallelProjectionGeometry2D*>(m_pProjectionGeometry) || dynamic_cast<CParallelVecProjectionGeometry2D*>(m_pProjectionGeometry), "ParallelBeamLineKernelProjector2D", "Unsupported projection geometry");
ASTRA_CONFIG_CHECK(m_pVolumeGeometry->getPixelLengthX() == m_pVolumeGeometry->getPixelLengthY(), "ParallelBeamLineKernelProjector2D", "Pixel height must equal pixel width.");
@@ -161,61 +161,63 @@ void CParallelBeamLineKernelProjector2D::computeSingleRayWeights(int _iProjectio
// Project Point
std::vector<SDetector2D> CParallelBeamLineKernelProjector2D::projectPoint(int _iRow, int _iCol)
{
- float32 xUL = m_pVolumeGeometry->pixelColToCenterX(_iCol) - m_pVolumeGeometry->getPixelLengthX() * 0.5f;
- float32 yUL = m_pVolumeGeometry->pixelRowToCenterY(_iRow) - m_pVolumeGeometry->getPixelLengthY() * 0.5f;
- float32 xUR = m_pVolumeGeometry->pixelColToCenterX(_iCol) + m_pVolumeGeometry->getPixelLengthX() * 0.5f;
- float32 yUR = m_pVolumeGeometry->pixelRowToCenterY(_iRow) - m_pVolumeGeometry->getPixelLengthY() * 0.5f;
- float32 xLL = m_pVolumeGeometry->pixelColToCenterX(_iCol) - m_pVolumeGeometry->getPixelLengthX() * 0.5f;
- float32 yLL = m_pVolumeGeometry->pixelRowToCenterY(_iRow) + m_pVolumeGeometry->getPixelLengthY() * 0.5f;
- float32 xLR = m_pVolumeGeometry->pixelColToCenterX(_iCol) + m_pVolumeGeometry->getPixelLengthX() * 0.5f;
- float32 yLR = m_pVolumeGeometry->pixelRowToCenterY(_iRow) + m_pVolumeGeometry->getPixelLengthY() * 0.5f;
-
std::vector<SDetector2D> res;
- // loop projectors and detectors
- for (int iProjection = 0; iProjection < m_pProjectionGeometry->getProjectionAngleCount(); ++iProjection) {
-
- // get projection angle
- float32 theta = m_pProjectionGeometry->getProjectionAngle(iProjection);
- if (theta >= 7*PIdiv4) theta -= 2*PI;
- bool inverse = false;
- if (theta >= 3*PIdiv4) {
- theta -= PI;
- inverse = true;
- }
-
- // calculate distance from the center of the voxel to the ray though the origin
- float32 tUL = xUL * cos(theta) + yUL * sin(theta);
- float32 tUR = xUR * cos(theta) + yUR * sin(theta);
- float32 tLL = xLL * cos(theta) + yLL * sin(theta);
- float32 tLR = xLR * cos(theta) + yLR * sin(theta);
- if (inverse) {
- tUL *= -1.0f;
- tUR *= -1.0f;
- tLL *= -1.0f;
- tLR *= -1.0f;
- }
- float32 tMin = min(tUL, min(tUR, min(tLL,tLR)));
- float32 tMax = max(tUL, max(tUR, max(tLL,tLR)));
-
- // calculate the offset on the detectorarray (in indices)
- int dmin = (int)floor(m_pProjectionGeometry->detectorOffsetToIndexFloat(tMin));
- int dmax = (int)ceil(m_pProjectionGeometry->detectorOffsetToIndexFloat(tMax));
-
- // add affected detectors to the list
- for (int i = dmin; i <= dmax; ++i) {
- if (i >= 0 && i < m_pProjectionGeometry->getDetectorCount()) {
- SDetector2D det;
- det.m_iAngleIndex = iProjection;
- det.m_iDetectorIndex = i;
- det.m_iIndex = iProjection * getProjectionGeometry()->getDetectorCount() + i;
- res.push_back(det);
- }
- }
- }
-
- // return result vector
return res;
+ // float32 xUL = m_pVolumeGeometry->pixelColToCenterX(_iCol) - m_pVolumeGeometry->getPixelLengthX() * 0.5f;
+ // float32 yUL = m_pVolumeGeometry->pixelRowToCenterY(_iRow) - m_pVolumeGeometry->getPixelLengthY() * 0.5f;
+ // float32 xUR = m_pVolumeGeometry->pixelColToCenterX(_iCol) + m_pVolumeGeometry->getPixelLengthX() * 0.5f;
+ // float32 yUR = m_pVolumeGeometry->pixelRowToCenterY(_iRow) - m_pVolumeGeometry->getPixelLengthY() * 0.5f;
+ // float32 xLL = m_pVolumeGeometry->pixelColToCenterX(_iCol) - m_pVolumeGeometry->getPixelLengthX() * 0.5f;
+ // float32 yLL = m_pVolumeGeometry->pixelRowToCenterY(_iRow) + m_pVolumeGeometry->getPixelLengthY() * 0.5f;
+ // float32 xLR = m_pVolumeGeometry->pixelColToCenterX(_iCol) + m_pVolumeGeometry->getPixelLengthX() * 0.5f;
+ // float32 yLR = m_pVolumeGeometry->pixelRowToCenterY(_iRow) + m_pVolumeGeometry->getPixelLengthY() * 0.5f;
+
+ // std::vector<SDetector2D> res;
+ // // loop projectors and detectors
+ // for (int iProjection = 0; iProjection < m_pProjectionGeometry->getProjectionAngleCount(); ++iProjection) {
+
+ // // get projection angle
+ // float32 theta = m_pProjectionGeometry->getProjectionAngle(iProjection);
+ // if (theta >= 7*PIdiv4) theta -= 2*PI;
+ // bool inverse = false;
+ // if (theta >= 3*PIdiv4) {
+ // theta -= PI;
+ // inverse = true;
+ // }
+
+ // // calculate distance from the center of the voxel to the ray though the origin
+ // float32 tUL = xUL * cos(theta) + yUL * sin(theta);
+ // float32 tUR = xUR * cos(theta) + yUR * sin(theta);
+ // float32 tLL = xLL * cos(theta) + yLL * sin(theta);
+ // float32 tLR = xLR * cos(theta) + yLR * sin(theta);
+ // if (inverse) {
+ // tUL *= -1.0f;
+ // tUR *= -1.0f;
+ // tLL *= -1.0f;
+ // tLR *= -1.0f;
+ // }
+ // float32 tMin = min(tUL, min(tUR, min(tLL,tLR)));
+ // float32 tMax = max(tUL, max(tUR, max(tLL,tLR)));
+
+ // // calculate the offset on the detectorarray (in indices)
+ // int dmin = (int)floor(m_pProjectionGeometry->detectorOffsetToIndexFloat(tMin));
+ // int dmax = (int)ceil(m_pProjectionGeometry->detectorOffsetToIndexFloat(tMax));
+
+ // // add affected detectors to the list
+ // for (int i = dmin; i <= dmax; ++i) {
+ // if (i >= 0 && i < m_pProjectionGeometry->getDetectorCount()) {
+ // SDetector2D det;
+ // det.m_iAngleIndex = iProjection;
+ // det.m_iDetectorIndex = i;
+ // det.m_iIndex = iProjection * getProjectionGeometry()->getDetectorCount() + i;
+ // res.push_back(det);
+ // }
+ // }
+ // }
+
+ // // return result vector
+ // return res;
}
//----------------------------------------------------------------------------------------
diff --git a/src/ParallelBeamLinearKernelProjector2D.cpp b/src/ParallelBeamLinearKernelProjector2D.cpp
index 27aa168..4090fb6 100644
--- a/src/ParallelBeamLinearKernelProjector2D.cpp
+++ b/src/ParallelBeamLinearKernelProjector2D.cpp
@@ -88,7 +88,7 @@ bool CParallelBeamLinearKernelProjector2D::_check()
// check base class
ASTRA_CONFIG_CHECK(CProjector2D::_check(), "ParallelBeamLinearKernelProjector2D", "Error in Projector2D initialization");
- ASTRA_CONFIG_CHECK(dynamic_cast<CParallelProjectionGeometry2D*>(m_pProjectionGeometry), "ParallelBeamLinearKernelProjector2D", "Unsupported projection geometry");
+ ASTRA_CONFIG_CHECK(dynamic_cast<CParallelProjectionGeometry2D*>(m_pProjectionGeometry) || dynamic_cast<CParallelVecProjectionGeometry2D*>(m_pProjectionGeometry), "ParallelBeamLinearKernelProjector2D", "Unsupported projection geometry");
/// TODO: ADD PIXEL H/W LIMITATIONS
ASTRA_CONFIG_CHECK(m_pVolumeGeometry->getPixelLengthX() == m_pVolumeGeometry->getPixelLengthY(), "ParallelBeamLinearKernelProjector2D", "Pixel height must equal pixel width.");
diff --git a/src/ParallelBeamStripKernelProjector2D.cpp b/src/ParallelBeamStripKernelProjector2D.cpp
index 3f4e7f3..68e4017 100644
--- a/src/ParallelBeamStripKernelProjector2D.cpp
+++ b/src/ParallelBeamStripKernelProjector2D.cpp
@@ -87,7 +87,7 @@ bool CParallelBeamStripKernelProjector2D::_check()
// check base class
ASTRA_CONFIG_CHECK(CProjector2D::_check(), "ParallelBeamStripKernelProjector2D", "Error in Projector2D initialization");
- ASTRA_CONFIG_CHECK(dynamic_cast<CParallelProjectionGeometry2D*>(m_pProjectionGeometry), "ParallelBeamStripKernelProjector2D", "Unsupported projection geometry");
+ ASTRA_CONFIG_CHECK(dynamic_cast<CParallelProjectionGeometry2D*>(m_pProjectionGeometry) || dynamic_cast<CParallelVecProjectionGeometry2D*>(m_pProjectionGeometry), "ParallelBeamStripKernelProjector2D", "Unsupported projection geometry");
ASTRA_CONFIG_CHECK(m_pVolumeGeometry->getPixelLengthX() == m_pVolumeGeometry->getPixelLengthY(), "ParallelBeamStripKernelProjector2D", "Pixel height must equal pixel width.");
@@ -163,57 +163,57 @@ void CParallelBeamStripKernelProjector2D::computeSingleRayWeights(int _iProjecti
// Splat a single point
std::vector<SDetector2D> CParallelBeamStripKernelProjector2D::projectPoint(int _iRow, int _iCol)
{
- float32 xUL = m_pVolumeGeometry->pixelColToCenterX(_iCol) - m_pVolumeGeometry->getPixelLengthX() * 0.5f;
- float32 yUL = m_pVolumeGeometry->pixelRowToCenterY(_iRow) - m_pVolumeGeometry->getPixelLengthY() * 0.5f;
- float32 xUR = m_pVolumeGeometry->pixelColToCenterX(_iCol) + m_pVolumeGeometry->getPixelLengthX() * 0.5f;
- float32 yUR = m_pVolumeGeometry->pixelRowToCenterY(_iRow) - m_pVolumeGeometry->getPixelLengthY() * 0.5f;
- float32 xLL = m_pVolumeGeometry->pixelColToCenterX(_iCol) - m_pVolumeGeometry->getPixelLengthX() * 0.5f;
- float32 yLL = m_pVolumeGeometry->pixelRowToCenterY(_iRow) + m_pVolumeGeometry->getPixelLengthY() * 0.5f;
- float32 xLR = m_pVolumeGeometry->pixelColToCenterX(_iCol) + m_pVolumeGeometry->getPixelLengthX() * 0.5f;
- float32 yLR = m_pVolumeGeometry->pixelRowToCenterY(_iRow) + m_pVolumeGeometry->getPixelLengthY() * 0.5f;
+ // float32 xUL = m_pVolumeGeometry->pixelColToCenterX(_iCol) - m_pVolumeGeometry->getPixelLengthX() * 0.5f;
+ // float32 yUL = m_pVolumeGeometry->pixelRowToCenterY(_iRow) - m_pVolumeGeometry->getPixelLengthY() * 0.5f;
+ // float32 xUR = m_pVolumeGeometry->pixelColToCenterX(_iCol) + m_pVolumeGeometry->getPixelLengthX() * 0.5f;
+ // float32 yUR = m_pVolumeGeometry->pixelRowToCenterY(_iRow) - m_pVolumeGeometry->getPixelLengthY() * 0.5f;
+ // float32 xLL = m_pVolumeGeometry->pixelColToCenterX(_iCol) - m_pVolumeGeometry->getPixelLengthX() * 0.5f;
+ // float32 yLL = m_pVolumeGeometry->pixelRowToCenterY(_iRow) + m_pVolumeGeometry->getPixelLengthY() * 0.5f;
+ // float32 xLR = m_pVolumeGeometry->pixelColToCenterX(_iCol) + m_pVolumeGeometry->getPixelLengthX() * 0.5f;
+ // float32 yLR = m_pVolumeGeometry->pixelRowToCenterY(_iRow) + m_pVolumeGeometry->getPixelLengthY() * 0.5f;
std::vector<SDetector2D> res;
- // loop projectors and detectors
- for (int iProjection = 0; iProjection < m_pProjectionGeometry->getProjectionAngleCount(); ++iProjection) {
-
- // get projection angle
- float32 theta = m_pProjectionGeometry->getProjectionAngle(iProjection);
- if (theta >= 7*PIdiv4) theta -= 2*PI;
- bool inverse = false;
- if (theta >= 3*PIdiv4) {
- theta -= PI;
- inverse = true;
- }
-
- // calculate distance from the center of the voxel to the ray though the origin
- float32 tUL = xUL * cos(theta) + yUL * sin(theta);
- float32 tUR = xUR * cos(theta) + yUR * sin(theta);
- float32 tLL = xLL * cos(theta) + yLL * sin(theta);
- float32 tLR = xLR * cos(theta) + yLR * sin(theta);
- if (inverse) {
- tUL *= -1.0f;
- tUR *= -1.0f;
- tLL *= -1.0f;
- tLR *= -1.0f;
- }
- float32 tMin = min(tUL, min(tUR, min(tLL,tLR)));
- float32 tMax = max(tUL, max(tUR, max(tLL,tLR)));
-
- // calculate the offset on the detectorarray (in indices)
- int dmin = (int)floor(m_pProjectionGeometry->detectorOffsetToIndexFloat(tMin));
- int dmax = (int)ceil(m_pProjectionGeometry->detectorOffsetToIndexFloat(tMax));
-
- // add affected detectors to the list
- for (int i = dmin; i <= dmax; ++i) {
- if (i >= 0 && i < m_pProjectionGeometry->getDetectorCount()) {
- SDetector2D det;
- det.m_iAngleIndex = iProjection;
- det.m_iDetectorIndex = i;
- det.m_iIndex = iProjection * getProjectionGeometry()->getDetectorCount() + i;
- res.push_back(det);
- }
- }
- }
+ // // loop projectors and detectors
+ // for (int iProjection = 0; iProjection < m_pProjectionGeometry->getProjectionAngleCount(); ++iProjection) {
+
+ // // get projection angle
+ // float32 theta = m_pProjectionGeometry->getProjectionAngle(iProjection);
+ // if (theta >= 7*PIdiv4) theta -= 2*PI;
+ // bool inverse = false;
+ // if (theta >= 3*PIdiv4) {
+ // theta -= PI;
+ // inverse = true;
+ // }
+
+ // // calculate distance from the center of the voxel to the ray though the origin
+ // float32 tUL = xUL * cos(theta) + yUL * sin(theta);
+ // float32 tUR = xUR * cos(theta) + yUR * sin(theta);
+ // float32 tLL = xLL * cos(theta) + yLL * sin(theta);
+ // float32 tLR = xLR * cos(theta) + yLR * sin(theta);
+ // if (inverse) {
+ // tUL *= -1.0f;
+ // tUR *= -1.0f;
+ // tLL *= -1.0f;
+ // tLR *= -1.0f;
+ // }
+ // float32 tMin = min(tUL, min(tUR, min(tLL,tLR)));
+ // float32 tMax = max(tUL, max(tUR, max(tLL,tLR)));
+
+ // // calculate the offset on the detectorarray (in indices)
+ // int dmin = (int)floor(m_pProjectionGeometry->detectorOffsetToIndexFloat(tMin));
+ // int dmax = (int)ceil(m_pProjectionGeometry->detectorOffsetToIndexFloat(tMax));
+
+ // // add affected detectors to the list
+ // for (int i = dmin; i <= dmax; ++i) {
+ // if (i >= 0 && i < m_pProjectionGeometry->getDetectorCount()) {
+ // SDetector2D det;
+ // det.m_iAngleIndex = iProjection;
+ // det.m_iDetectorIndex = i;
+ // det.m_iIndex = iProjection * getProjectionGeometry()->getDetectorCount() + i;
+ // res.push_back(det);
+ // }
+ // }
+ // }
// return result vector
return res;
diff --git a/src/ParallelProjectionGeometry2D.cpp b/src/ParallelProjectionGeometry2D.cpp
index cc2a129..105e24b 100644
--- a/src/ParallelProjectionGeometry2D.cpp
+++ b/src/ParallelProjectionGeometry2D.cpp
@@ -66,8 +66,7 @@ CParallelProjectionGeometry2D::CParallelProjectionGeometry2D(const CParallelProj
initialize(_projGeom.m_iProjectionAngleCount,
_projGeom.m_iDetectorCount,
_projGeom.m_fDetectorWidth,
- _projGeom.m_pfProjectionAngles,
- _projGeom.m_pfExtraDetectorOffset);
+ _projGeom.m_pfProjectionAngles);
}
//----------------------------------------------------------------------------------------
@@ -186,8 +185,8 @@ Config* CParallelProjectionGeometry2D::getConfiguration() const
}
return cfg;
}
-//----------------------------------------------------------------------------------------
+//----------------------------------------------------------------------------------------
CVector3D CParallelProjectionGeometry2D::getProjectionDirection(int _iProjectionIndex, int _iDetectorIndex /* = 0 */)
{
CVector3D vOutput;
@@ -201,4 +200,24 @@ CVector3D CParallelProjectionGeometry2D::getProjectionDirection(int _iProjection
return vOutput;
}
+//----------------------------------------------------------------------------------------
+CParallelVecProjectionGeometry2D* CParallelProjectionGeometry2D::toVectorGeometry()
+{
+ SParProjection* vectors = new SParProjection[m_iProjectionAngleCount];
+ for (int i = 0; i < m_iProjectionAngleCount; ++i)
+ {
+ vectors[i].fRayX = sinf(m_pfProjectionAngles[i]);
+ vectors[i].fRayY = -cosf(m_pfProjectionAngles[i]);
+ vectors[i].fDetUX = cosf(m_pfProjectionAngles[i]) * m_fDetectorWidth;
+ vectors[i].fDetUY = sinf(m_pfProjectionAngles[i]) * m_fDetectorWidth;
+ vectors[i].fDetSX = -0.5f * m_iDetectorCount * vectors[i].fDetUX;
+ vectors[i].fDetSY = -0.5f * m_iDetectorCount * vectors[i].fDetUY;
+ }
+
+ CParallelVecProjectionGeometry2D* vecGeom = new CParallelVecProjectionGeometry2D();
+ vecGeom->initialize(m_iProjectionAngleCount, m_iDetectorCount, vectors);
+ delete[] vectors;
+ return vecGeom;
+}
+
} // end namespace astra
diff --git a/src/ParallelVecProjectionGeometry2D.cpp b/src/ParallelVecProjectionGeometry2D.cpp
new file mode 100644
index 0000000..1503076
--- /dev/null
+++ b/src/ParallelVecProjectionGeometry2D.cpp
@@ -0,0 +1,233 @@
+/*
+-----------------------------------------------------------------------
+Copyright: 2010-2015, iMinds-Vision Lab, University of Antwerp
+ 2014-2015, CWI, Amsterdam
+
+Contact: astra@uantwerpen.be
+Website: http://sf.net/projects/astra-toolbox
+
+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 <http://www.gnu.org/licenses/>.
+
+-----------------------------------------------------------------------
+$Id$
+*/
+
+#include "astra/ParallelVecProjectionGeometry2D.h"
+
+#include <cstring>
+#include <sstream>
+#include <boost/lexical_cast.hpp>
+
+using namespace std;
+
+namespace astra
+{
+
+//----------------------------------------------------------------------------------------
+// Default constructor. Sets all variables to zero.
+CParallelVecProjectionGeometry2D::CParallelVecProjectionGeometry2D()
+{
+ _clear();
+ m_pProjectionAngles = 0;
+}
+
+//----------------------------------------------------------------------------------------
+// Constructor.
+CParallelVecProjectionGeometry2D::CParallelVecProjectionGeometry2D(int _iProjectionAngleCount,
+ int _iDetectorCount,
+ const SParProjection* _pProjectionAngles)
+{
+ this->initialize(_iProjectionAngleCount,
+ _iDetectorCount,
+ _pProjectionAngles);
+}
+
+//----------------------------------------------------------------------------------------
+// Copy Constructor
+CParallelVecProjectionGeometry2D::CParallelVecProjectionGeometry2D(const CParallelVecProjectionGeometry2D& _projGeom)
+{
+ _clear();
+ this->initialize(_projGeom.m_iProjectionAngleCount,
+ _projGeom.m_iDetectorCount,
+ _projGeom.m_pProjectionAngles);
+}
+
+//----------------------------------------------------------------------------------------
+// Destructor.
+CParallelVecProjectionGeometry2D::~CParallelVecProjectionGeometry2D()
+{
+ // TODO
+ delete[] m_pProjectionAngles;
+}
+
+
+//----------------------------------------------------------------------------------------
+// Initialization.
+bool CParallelVecProjectionGeometry2D::initialize(int _iProjectionAngleCount,
+ int _iDetectorCount,
+ const SParProjection* _pProjectionAngles)
+{
+ m_iProjectionAngleCount = _iProjectionAngleCount;
+ m_iDetectorCount = _iDetectorCount;
+ m_pProjectionAngles = new SParProjection[m_iProjectionAngleCount];
+ for (int i = 0; i < m_iProjectionAngleCount; ++i)
+ m_pProjectionAngles[i] = _pProjectionAngles[i];
+
+ // TODO: check?
+
+ // success
+ m_bInitialized = _check();
+ return m_bInitialized;
+}
+
+//----------------------------------------------------------------------------------------
+// Initialization with a Config object
+bool CParallelVecProjectionGeometry2D::initialize(const Config& _cfg)
+{
+ ASTRA_ASSERT(_cfg.self);
+ ConfigStackCheck<CProjectionGeometry2D> CC("ParallelVecProjectionGeometry2D", this, _cfg);
+
+ // TODO: Fix up class hierarchy... this class doesn't fit very well.
+ // initialization of parent class
+ //CProjectionGeometry2D::initialize(_cfg);
+
+ // Required: DetectorCount
+ XMLNode node = _cfg.self.getSingleNode("DetectorCount");
+ ASTRA_CONFIG_CHECK(node, "ParallelVecProjectionGeometry2D", "No DetectorRowCount tag specified.");
+ m_iDetectorCount = boost::lexical_cast<int>(node.getContent());
+ CC.markNodeParsed("DetectorCount");
+
+ // Required: Vectors
+ node = _cfg.self.getSingleNode("Vectors");
+ ASTRA_CONFIG_CHECK(node, "ParallelVecProjectionGeometry2D", "No Vectors tag specified.");
+ vector<float32> data = node.getContentNumericalArray();
+ CC.markNodeParsed("Vectors");
+ ASTRA_CONFIG_CHECK(data.size() % 6 == 0, "ParallelVecProjectionGeometry2D", "Vectors doesn't consist of 6-tuples.");
+ m_iProjectionAngleCount = data.size() / 6;
+ m_pProjectionAngles = new SParProjection[m_iProjectionAngleCount];
+
+ for (int i = 0; i < m_iProjectionAngleCount; ++i) {
+ SParProjection& p = m_pProjectionAngles[i];
+ p.fRayX = data[6*i + 0];
+ p.fRayY = data[6*i + 1];
+ p.fDetUX = data[6*i + 4];
+ p.fDetUY = data[6*i + 5];
+
+ // The backend code currently expects the corner of the detector, while
+ // the matlab interface supplies the center
+ p.fDetSX = data[6*i + 2] - 0.5f * m_iDetectorCount * p.fDetUX;
+ p.fDetSY = data[6*i + 3] - 0.5f * m_iDetectorCount * p.fDetUY;
+ }
+
+ // success
+ m_bInitialized = _check();
+ return m_bInitialized;
+}
+
+//----------------------------------------------------------------------------------------
+// Clone
+CProjectionGeometry2D* CParallelVecProjectionGeometry2D::clone()
+{
+ return new CParallelVecProjectionGeometry2D(*this);
+}
+
+//----------------------------------------------------------------------------------------
+// is equal
+bool CParallelVecProjectionGeometry2D::isEqual(CProjectionGeometry2D* _pGeom2) const
+{
+ if (_pGeom2 == NULL) return false;
+
+ // try to cast argument to CParallelVecProjectionGeometry2D
+ CParallelVecProjectionGeometry2D* pGeom2 = dynamic_cast<CParallelVecProjectionGeometry2D*>(_pGeom2);
+ if (pGeom2 == NULL) return false;
+
+ // both objects must be initialized
+ if (!m_bInitialized || !pGeom2->m_bInitialized) return false;
+
+ // check all values
+ if (m_iProjectionAngleCount != pGeom2->m_iProjectionAngleCount) return false;
+ if (m_iDetectorCount != pGeom2->m_iDetectorCount) return false;
+
+ for (int i = 0; i < m_iProjectionAngleCount; ++i) {
+ if (memcmp(&m_pProjectionAngles[i], &pGeom2->m_pProjectionAngles[i], sizeof(m_pProjectionAngles[i])) != 0) return false;
+ }
+
+ return true;
+}
+
+//----------------------------------------------------------------------------------------
+// Is of type
+bool CParallelVecProjectionGeometry2D::isOfType(const std::string& _sType)
+{
+ return (_sType == "parallel_vec");
+}
+
+//----------------------------------------------------------------------------------------
+
+CVector3D CParallelVecProjectionGeometry2D::getProjectionDirection(int _iProjectionIndex, int _iDetectorIndex /* = 0 */)
+{
+ CVector3D vOutput(0.0f, 0.0f, 0.0f);
+
+ // not implemented
+ ASTRA_ASSERT(false);
+
+ return vOutput;
+}
+
+//----------------------------------------------------------------------------------------
+
+void CParallelVecProjectionGeometry2D::getRayParams(int _iRow, int _iColumn, float32& _fT, float32& _fTheta) const
+{
+ // not implemented
+ ASTRA_ASSERT(false);
+}
+
+//----------------------------------------------------------------------------------------
+
+bool CParallelVecProjectionGeometry2D::_check()
+{
+ // TODO
+ return true;
+}
+
+
+//----------------------------------------------------------------------------------------
+// Get the configuration object
+Config* CParallelVecProjectionGeometry2D::getConfiguration() const
+{
+ Config* cfg = new Config();
+ cfg->initialize("ProjectionGeometry2D");
+ cfg->self.addAttribute("type", "parallel_vec");
+ cfg->self.addChildNode("DetectorCount", getDetectorCount());
+ std::string vectors = "";
+ for (int i = 0; i < m_iProjectionAngleCount; ++i) {
+ SParProjection& p = m_pProjectionAngles[i];
+ vectors += boost::lexical_cast<string>(p.fRayX) + ",";
+ vectors += boost::lexical_cast<string>(p.fRayY) + ",";
+ vectors += boost::lexical_cast<string>(p.fDetSX + 0.5f * m_iDetectorCount * p.fDetUX) + ",";
+ vectors += boost::lexical_cast<string>(p.fDetSY + 0.5f * m_iDetectorCount * p.fDetUY) + ",";
+ vectors += boost::lexical_cast<string>(p.fDetUX) + ",";
+ vectors += boost::lexical_cast<string>(p.fDetUY);
+ if (i < m_iProjectionAngleCount-1) vectors += ';';
+ }
+ cfg->self.addChildNode("Vectors", vectors);
+ return cfg;
+}
+//----------------------------------------------------------------------------------------
+
+
+} // namespace astra
diff --git a/src/ProjectionGeometry2D.cpp b/src/ProjectionGeometry2D.cpp
index 8ce06dc..03fbd22 100644
--- a/src/ProjectionGeometry2D.cpp
+++ b/src/ProjectionGeometry2D.cpp
@@ -70,7 +70,6 @@ void CProjectionGeometry2D::_clear()
m_iDetectorCount = 0;
m_fDetectorWidth = 0.0f;
m_pfProjectionAngles = NULL;
- m_pfExtraDetectorOffset = NULL;
m_bInitialized = false;
}
@@ -83,10 +82,8 @@ void CProjectionGeometry2D::clear()
m_fDetectorWidth = 0.0f;
if (m_bInitialized){
delete[] m_pfProjectionAngles;
- delete[] m_pfExtraDetectorOffset;
}
m_pfProjectionAngles = NULL;
- m_pfExtraDetectorOffset = NULL;
m_bInitialized = false;
}
@@ -145,19 +142,6 @@ bool CProjectionGeometry2D::initialize(const Config& _cfg)
}
CC.markNodeParsed("ProjectionAngles");
- vector<float32> offset = _cfg.self.getOptionNumericalArray("ExtraDetectorOffset");
- m_pfExtraDetectorOffset = new float32[m_iProjectionAngleCount];
- if (offset.size() == (size_t)m_iProjectionAngleCount) {
- for (int i = 0; i < m_iProjectionAngleCount; i++) {
- m_pfExtraDetectorOffset[i] = offset[i];
- }
- } else {
- for (int i = 0; i < m_iProjectionAngleCount; i++) {
- m_pfExtraDetectorOffset[i] = 0.0f;
- }
- }
- CC.markOptionParsed("ExtraDetectorOffset");
-
// some checks
ASTRA_CONFIG_CHECK(m_iDetectorCount > 0, "ProjectionGeometry2D", "DetectorCount should be positive.");
ASTRA_CONFIG_CHECK(m_fDetectorWidth > 0.0f, "ProjectionGeometry2D", "DetectorWidth should be positive.");
@@ -184,10 +168,8 @@ bool CProjectionGeometry2D::_initialize(int _iProjectionAngleCount,
m_iDetectorCount = _iDetectorCount;
m_fDetectorWidth = _fDetectorWidth;
m_pfProjectionAngles = new float32[m_iProjectionAngleCount];
- m_pfExtraDetectorOffset = new float32[m_iProjectionAngleCount];
for (int i = 0; i < m_iProjectionAngleCount; i++) {
m_pfProjectionAngles[i] = _pfProjectionAngles[i];
- m_pfExtraDetectorOffset[i] = _pfExtraDetectorOffsets ? _pfExtraDetectorOffsets[i]:0;
}
// Interface class, so don't set m_bInitialized to true
diff --git a/src/Projector2D.cpp b/src/Projector2D.cpp
index cf233a0..c8743b5 100644
--- a/src/Projector2D.cpp
+++ b/src/Projector2D.cpp
@@ -28,6 +28,7 @@ $Id$
#include "astra/Projector2D.h"
+#include "astra/ParallelVecProjectionGeometry2D.h"
#include "astra/FanFlatProjectionGeometry2D.h"
#include "astra/FanFlatVecProjectionGeometry2D.h"
#include "astra/SparseMatrixProjectionGeometry2D.h"
@@ -131,6 +132,10 @@ bool CProjector2D::initialize(const Config& _cfg)
CFanFlatVecProjectionGeometry2D* pFanFlatVecProjectionGeometry = new CFanFlatVecProjectionGeometry2D();
pFanFlatVecProjectionGeometry->initialize(Config(node));
m_pProjectionGeometry = pFanFlatVecProjectionGeometry;
+ } else if (type == "parallel_vec") {
+ CParallelVecProjectionGeometry2D* pParallelVecProjectionGeometry = new CParallelVecProjectionGeometry2D();
+ pParallelVecProjectionGeometry->initialize(Config(node));
+ m_pProjectionGeometry = pParallelVecProjectionGeometry;
} else {
m_pProjectionGeometry = new CParallelProjectionGeometry2D();
m_pProjectionGeometry->initialize(Config(node));