diff options
45 files changed, 1874 insertions, 1235 deletions
diff --git a/astra_vc11.vcxproj b/astra_vc11.vcxproj index 6596a05..91b216e 100644 --- a/astra_vc11.vcxproj +++ b/astra_vc11.vcxproj @@ -263,6 +263,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\ProjectionGeometry2D.cpp" /> @@ -375,6 +376,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\ProjectionGeometry2D.h" /> <ClInclude Include="include\astra\ProjectionGeometry3D.h" /> @@ -438,4 +440,4 @@ <ImportGroup Label="ExtensionTargets"> <Import Project="$(VCTargetsPath)\BuildCustomizations\CUDA 5.5.targets" /> </ImportGroup> -</Project> +</Project>
\ No newline at end of file diff --git a/astra_vc11.vcxproj.filters b/astra_vc11.vcxproj.filters index c4ba594..0453ec7 100644 --- a/astra_vc11.vcxproj.filters +++ b/astra_vc11.vcxproj.filters @@ -37,9 +37,6 @@ <CudaCompile Include="cuda\3d\darthelper3d.cu"> <Filter>CUDA\cuda source</Filter> </CudaCompile> - <CudaCompile Include="cuda\3d\dims3d.cu"> - <Filter>CUDA\cuda source</Filter> - </CudaCompile> <CudaCompile Include="cuda\2d\em.cu"> <Filter>CUDA\cuda source</Filter> </CudaCompile> @@ -312,6 +309,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"> @@ -644,6 +645,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"> @@ -736,4 +742,4 @@ <UniqueIdentifier>{04a878ed-77b4-4525-9bc2-38ccd65282c5}</UniqueIdentifier> </Filter> </ItemGroup> -</Project> +</Project>
\ No newline at end of file diff --git a/build/linux/Makefile.in b/build/linux/Makefile.in index f647299..d391278 100644 --- a/build/linux/Makefile.in +++ b/build/linux/Makefile.in @@ -129,6 +129,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 0b5be06..dc16a56 100644 --- a/cuda/2d/astra.cu +++ b/cuda/2d/astra.cu @@ -827,17 +827,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..1676cb1 100644 --- a/include/astra/FanFlatBeamLineKernelProjector2D.inl +++ b/include/astra/FanFlatBeamLineKernelProjector2D.inl @@ -51,246 +51,201 @@ 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; - - const CFanFlatProjectionGeometry2D* pProjectionGeometry = dynamic_cast<CFanFlatProjectionGeometry2D*>(m_pProjectionGeometry); - const CFanFlatVecProjectionGeometry2D* pVecProjectionGeometry = dynamic_cast<CFanFlatVecProjectionGeometry2D*>(m_pProjectionGeometry); - - float32 old_theta, theta, alpha; + float32 detX, detY, S, T, I, x, y, c, r, update_c, update_r, offset; + float32 lengthPerRow, lengthPerCol, inv_pixelLengthX, inv_pixelLengthY, invTminSTimesLengthPerRow, invTminSTimesLengthPerCol; + int iVolumeIndex, iRayIndex, row, col, iAngle, iDetector, colCount, rowCount, detCount; const SFanProjection * proj = 0; + // 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 + inv_pixelLengthX = 1.0f / m_pVolumeGeometry->getPixelLengthX(); + inv_pixelLengthY = 1.0f / m_pVolumeGeometry->getPixelLengthY(); + colCount = m_pVolumeGeometry->getGridColCount(); + rowCount = m_pVolumeGeometry->getGridRowCount(); + detCount = pVecProjectionGeometry->getDetectorCount(); + // 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; - } + 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); + + detX = proj->fDetSX + (iDetector+0.5f) * proj->fDetUX; + detY = proj->fDetSY + (iDetector+0.5f) * proj->fDetUY; + + float32 fRayX = proj->fSrcX - detX; + float32 fRayY = proj->fSrcY - detY; + + bool vertical = fabs(fRayX) < fabs(fRayY); + if (vertical) { + lengthPerRow = m_pVolumeGeometry->getPixelLengthX() * sqrt(fRayY*fRayY + fRayX*fRayX) / abs(fRayY); + update_c = -m_pVolumeGeometry->getPixelLengthY() * (fRayX/fRayY) * inv_pixelLengthX; + S = 0.5f - 0.5f*fabs(fRayX/fRayY); + T = 0.5f + 0.5f*fabs(fRayX/fRayY); + invTminSTimesLengthPerRow = lengthPerRow / (T - S); } else { - assert(false); + lengthPerCol = m_pVolumeGeometry->getPixelLengthY() * sqrt(fRayY*fRayY + fRayX*fRayX) / abs(fRayX); + update_r = -m_pVolumeGeometry->getPixelLengthX() * (fRayY/fRayX) * inv_pixelLengthY; + S = 0.5f - 0.5f*fabs(fRayY/fRayX); + T = 0.5f + 0.5f*fabs(fRayY/fRayX); + 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); - // vertically - if (old_theta <= PIdiv4) { - + if (vertical) { + // calculate x for row 0 - P = (t - sin_theta * m_pVolumeGeometry->pixelRowToCenterY(0)) * inv_cos_theta; - x = (P - m_pVolumeGeometry->getWindowMinX()) * inv_pixelLengthX; + x = detX + (fRayX/fRayY)*(m_pVolumeGeometry->pixelRowToCenterY(0)-detY); + c = (x - m_pVolumeGeometry->getWindowMinX()) * inv_pixelLengthX - 0.5f; // 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 += update_c) { + + col = int(c+0.5f); + offset = c - float32(col); - if (x1 < -1 || x1 > m_pVolumeGeometry->getGridColCount()) continue; + if (col <= 0 || col >= colCount-1) continue; // 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 (offset < -S) { + I = (offset + T) * invTminSTimesLengthPerRow; + + iVolumeIndex = row * colCount + col - 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); - } + iVolumeIndex++; + // POLICY: PIXEL PRIOR + ADD + POSTERIOR + if (p.pixelPrior(iVolumeIndex)) { + p.addWeight(iRayIndex, iVolumeIndex, I); + 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 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); - } + else if (S < offset) { + I = (offset - S) * invTminSTimesLengthPerRow; + + iVolumeIndex = row * colCount + col; + // POLICY: PIXEL PRIOR + ADD + POSTERIOR + if (p.pixelPrior(iVolumeIndex)) { + p.addWeight(iRayIndex, iVolumeIndex, lengthPerRow-I); + p.pixelPosterior(iVolumeIndex); + } + + iVolumeIndex++; + // 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); - } + } + + // centre + else { + iVolumeIndex = row * colCount + col; + // POLICY: PIXEL PRIOR + ADD + POSTERIOR + if (p.pixelPrior(iVolumeIndex)) { + p.addWeight(iRayIndex, iVolumeIndex, lengthPerRow); + p.pixelPosterior(iVolumeIndex); } } + } } // 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 y for col 0 + y = detY + (fRayY/fRayX)*(m_pVolumeGeometry->pixelColToCenterX(0)-detX); + r = (m_pVolumeGeometry->getWindowMaxY() - y) * inv_pixelLengthY - 0.5f; // 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 += update_r) { + + int row = int(r+0.5f); + offset = r - float32(row); - if (x1 < -1 || x1 > m_pVolumeGeometry->getGridRowCount()) continue; + if (row <= 0 || row >= rowCount-1) continue; // 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 (offset < -S) { + I = (offset + T) * invTminSTimesLengthPerCol; + + iVolumeIndex = (row-1) * colCount + 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); - } + iVolumeIndex += colCount; + // POLICY: PIXEL PRIOR + ADD + POSTERIOR + if (p.pixelPrior(iVolumeIndex)) { + p.addWeight(iRayIndex, iVolumeIndex, I); + p.pixelPosterior(iVolumeIndex); } } - // 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); - } - } - } - // 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); - } + else if (S < offset) { + I = (offset - S) * invTminSTimesLengthPerCol; + + iVolumeIndex = row * colCount + col; + // POLICY: PIXEL PRIOR + ADD + POSTERIOR + if (p.pixelPrior(iVolumeIndex)) { + p.addWeight(iRayIndex, iVolumeIndex, lengthPerCol-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); - } + + iVolumeIndex += colCount; + // POLICY: PIXEL PRIOR + ADD + POSTERIOR + if (p.pixelPrior(iVolumeIndex)) { + p.addWeight(iRayIndex, iVolumeIndex, I); + p.pixelPosterior(iVolumeIndex); + } + } + + // centre + else { + iVolumeIndex = row * colCount + col; + // POLICY: PIXEL PRIOR + ADD + POSTERIOR + if (p.pixelPrior(iVolumeIndex)) { + p.addWeight(iRayIndex, iVolumeIndex, lengthPerCol); + p.pixelPosterior(iVolumeIndex); } } + } - } // 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..203eb51 100644 --- a/include/astra/ParallelBeamBlobKernelProjector2D.inl +++ b/include/astra/ParallelBeamBlobKernelProjector2D.inl @@ -27,186 +27,151 @@ $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); - - int iRayIndex = _iProjection * m_pProjectionGeometry->getDetectorCount() + _iDetector; - - // POLICY: RAY PRIOR - if (!p.rayPrior(iRayIndex)) return; - - // get values - float32 t = m_pProjectionGeometry->indexToDetectorOffset(_iDetector); - float32 theta = m_pProjectionGeometry->getProjectionAngle(_iProjection); - if (theta >= 7*PIdiv4) theta -= 2*PI; - - bool flip = false; + projectBlock_internal(_iProjection, _iProjection + 1, + _iDetector, _iDetector + 1, p); +} - if (theta >= 3*PIdiv4) { - theta -= PI; - t = -t; - flip = true; +//---------------------------------------------------------------------------------------- +// PROJECT BLOCK - vector projection geometry +template <typename Policy> +void CParallelBeamBlobKernelProjector2D::projectBlock_internal(int _iProjFrom, int _iProjTo, int _iDetFrom, int _iDetTo, Policy& p) +{ + // variables + float32 detX, detY, x, y, c, r, update_c, update_r, offset, invBlobExtent, inv_pixelLengthX, inv_pixelLengthY, weight, d; + int iVolumeIndex, iRayIndex, row, col, iAngle, iDetector, colCount, rowCount, detCount; + int col_left, col_right, row_top, row_bottom, index; + const SParProjection * proj = 0; + + // 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 + inv_pixelLengthX = 1.0f / m_pVolumeGeometry->getPixelLengthX(); + inv_pixelLengthY = 1.0f / m_pVolumeGeometry->getPixelLengthY(); + colCount = m_pVolumeGeometry->getGridColCount(); + rowCount = m_pVolumeGeometry->getGridRowCount(); + detCount = pVecProjectionGeometry->getDetectorCount(); + + // loop angles + for (iAngle = _iProjFrom; iAngle < _iProjTo; ++iAngle) { + + proj = &pVecProjectionGeometry->getProjectionVectors()[iAngle]; + + bool vertical = fabs(proj->fRayX) < fabs(proj->fRayY); + if (vertical) { + update_c = -m_pVolumeGeometry->getPixelLengthY() * (proj->fRayX/proj->fRayY) * inv_pixelLengthX; + float32 normR = sqrt(proj->fRayY*proj->fRayY + proj->fRayX*proj->fRayX); + invBlobExtent = m_pVolumeGeometry->getPixelLengthY() / abs(m_fBlobSize * normR / proj->fRayY); + } else { + update_r = -m_pVolumeGeometry->getPixelLengthX() * (proj->fRayY/proj->fRayX) * inv_pixelLengthY; + float32 normR = sqrt(proj->fRayY*proj->fRayY + proj->fRayX*proj->fRayX); + invBlobExtent = m_pVolumeGeometry->getPixelLengthX() / abs(m_fBlobSize * normR / proj->fRayX); + } - if (theta <= PIdiv4) { // -pi/4 <= theta <= pi/4 - - // precalculate sin, cos, 1/cos - float32 sin_theta = sin(theta); - float32 cos_theta = cos(theta); - float32 inv_cos_theta = 1.0f / cos_theta; - - // 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; - - // 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; - - // for each row - for (row = 0; row < m_pVolumeGeometry->getGridRowCount(); ++row) { + // loop detectors + for (iDetector = _iDetFrom; iDetector < _iDetTo; ++iDetector) { - // 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)]; - } + iRayIndex = iAngle * m_pProjectionGeometry->getDetectorCount() + iDetector; - int iVolumeIndex = m_pVolumeGeometry->pixelRowColToIndex(row, col); - // POLICY: PIXEL PRIOR + ADD + POSTERIOR - if (p.pixelPrior(iVolumeIndex)) { - p.addWeight(iRayIndex, iVolumeIndex, fWeight); - p.pixelPosterior(iVolumeIndex); + // POLICY: RAY PRIOR + if (!p.rayPrior(iRayIndex)) continue; + + detX = proj->fDetSX + (iDetector+0.5f) * proj->fDetUX; + detY = proj->fDetSY + (iDetector+0.5f) * proj->fDetUY; + + // vertically + if (vertical) { + + // calculate x for row 0 + x = detX + (proj->fRayX/proj->fRayY)*(m_pVolumeGeometry->pixelRowToCenterY(0)-detY); + c = (x - m_pVolumeGeometry->getWindowMinX()) * inv_pixelLengthX - 0.5f; + + // for each row + for (row = 0; row < rowCount; ++row, c += update_c) { + + col_left = int(c - 0.5f - m_fBlobSize); + col_right = int(c + 0.5f + m_fBlobSize); + + if (col_left < 0) col_left = 0; + if (col_right > colCount-1) col_right = colCount-1; + + // for each column + for (col = col_left; col <= col_right; ++col) { + + offset = abs(c - float32(col)) * invBlobExtent; + index = (int)(offset*m_iBlobSampleCount+0.5f); + weight = m_pfBlobValues[min(index,m_iBlobSampleCount-1)]; + + iVolumeIndex = row * colCount + col; + // POLICY: PIXEL PRIOR + ADD + POSTERIOR + if (p.pixelPrior(iVolumeIndex)) { + p.addWeight(iRayIndex, iVolumeIndex, weight); + 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)]; - } + // calculate y for col 0 + y = detY + (proj->fRayY/proj->fRayX)*(m_pVolumeGeometry->pixelColToCenterX(0)-detX); + r = (m_pVolumeGeometry->getWindowMaxY() - y) * inv_pixelLengthY - 0.5f; + // for each col + for (col = 0; col < colCount; ++col, r += update_r) { - int iVolumeIndex = m_pVolumeGeometry->pixelRowColToIndex(row, col); - // POLICY: PIXEL PRIOR + ADD + POSTERIOR - if (p.pixelPrior(iVolumeIndex)) { - p.addWeight(iRayIndex, iVolumeIndex, fWeight); - p.pixelPosterior(iVolumeIndex); - } - } - } + row_top = int(r - 0.5f - m_fBlobSize); + row_bottom = int(r + 0.5f + m_fBlobSize); - // update P and x - P += updatePerCol; - x += updatePerCol * inv_pixelLengthY; - } + if (row_top < 0) row_top = 0; + if (row_bottom > rowCount-1) row_bottom = rowCount-1; - } - - // POLICY: RAY POSTERIOR - p.rayPosterior(iRayIndex); + // for each column + for (row = row_top; row <= row_bottom; ++row) { + offset = abs(r - float32(row)) * invBlobExtent; + index = (int)(offset*m_iBlobSampleCount+0.5f); + weight = m_pfBlobValues[min(index,m_iBlobSampleCount-1)]; + + iVolumeIndex = row * colCount + col; + // POLICY: PIXEL PRIOR + ADD + POSTERIOR + if (p.pixelPrior(iVolumeIndex)) { + p.addWeight(iRayIndex, iVolumeIndex, weight); + 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 199d69e..dc3153d 100644 --- a/include/astra/ParallelBeamLineKernelProjector2D.inl +++ b/include/astra/ParallelBeamLineKernelProjector2D.inl @@ -30,7 +30,7 @@ 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> @@ -48,49 +48,54 @@ void CParallelBeamLineKernelProjector2D::projectSingleRay(int _iProjection, int } + + //---------------------------------------------------------------------------------------- -// PROJECT BLOCK +// PROJECT BLOCK - vector projection geometry 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; + float32 detX, detY, S, T, I, x, y, c, r, update_c, update_r, offset; + float32 lengthPerRow, lengthPerCol, inv_pixelLengthX, inv_pixelLengthY, invTminSTimesLengthPerRow, invTminSTimesLengthPerCol; + int iVolumeIndex, iRayIndex, row, col, iAngle, iDetector, colCount, rowCount, detCount; + const SParProjection * proj = 0; + + // 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 + inv_pixelLengthX = 1.0f / m_pVolumeGeometry->getPixelLengthX(); + inv_pixelLengthY = 1.0f / m_pVolumeGeometry->getPixelLengthY(); + colCount = m_pVolumeGeometry->getGridColCount(); + rowCount = m_pVolumeGeometry->getGridRowCount(); + 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; + proj = &pVecProjectionGeometry->getProjectionVectors()[iAngle]; + + bool vertical = fabs(proj->fRayX) < fabs(proj->fRayY); + if (vertical) { + lengthPerRow = m_pVolumeGeometry->getPixelLengthX() * sqrt(proj->fRayY*proj->fRayY + proj->fRayX*proj->fRayX) / abs(proj->fRayY); + update_c = -m_pVolumeGeometry->getPixelLengthY() * (proj->fRayX/proj->fRayY) * inv_pixelLengthX; + S = 0.5f - 0.5f*fabs(proj->fRayX/proj->fRayY); + T = 0.5f + 0.5f*fabs(proj->fRayX/proj->fRayY); + invTminSTimesLengthPerRow = lengthPerRow / (T - S); + } else { + lengthPerCol = m_pVolumeGeometry->getPixelLengthY() * sqrt(proj->fRayY*proj->fRayY + proj->fRayX*proj->fRayX) / abs(proj->fRayX); + update_r = -m_pVolumeGeometry->getPixelLengthX() * (proj->fRayY/proj->fRayX) * inv_pixelLengthY; + S = 0.5f - 0.5f*fabs(proj->fRayY/proj->fRayX); + T = 0.5f + 0.5f*fabs(proj->fRayY/proj->fRayX); + 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); - // loop detectors for (iDetector = _iDetFrom; iDetector < _iDetTo; ++iDetector) { @@ -99,185 +104,140 @@ void CParallelBeamLineKernelProjector2D::projectBlock_internal(int _iProjFrom, i // POLICY: RAY PRIOR if (!p.rayPrior(iRayIndex)) continue; - // get t - t = m_pProjectionGeometry->indexToDetectorOffset(iDetector); - if (switch_t) t = -t; + detX = proj->fDetSX + (iDetector+0.5f) * proj->fDetUX; + detY = proj->fDetSY + (iDetector+0.5f) * proj->fDetUY; // 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; + if (vertical) { - // get coords - int nextx1 = int((x > 0.0f) ? x : x-1.0f); - float nextx2 = x - nextx1; + // calculate x for row 0 + x = detX + (proj->fRayX/proj->fRayY)*(m_pVolumeGeometry->pixelRowToCenterY(0)-detY); + c = (x - m_pVolumeGeometry->getWindowMinX()) * inv_pixelLengthX - 0.5f; // for each row - for (row = 0; row < m_pVolumeGeometry->getGridRowCount(); ++row) { + for (row = 0; row < rowCount; ++row, c += update_c) { - x1 = nextx1; - x2 = nextx2; - - nextx2 += updatePerRow; - while (nextx2 >= 1.0f) { - nextx2 -= 1.0f; - nextx1++; - } - while (nextx2 < 0.0f) { - nextx2 += 1.0f; - nextx1--; - } + col = int(c+0.5f); + offset = c - float32(col); - if (x1 < -1 || x1 > m_pVolumeGeometry->getGridColCount()) continue; + if (col <= 0 || col >= colCount-1) continue; // 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) { + I = (offset + T) * invTminSTimesLengthPerRow; + + iVolumeIndex = row * colCount + col - 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); - } + iVolumeIndex++; + // POLICY: PIXEL PRIOR + ADD + POSTERIOR + if (p.pixelPrior(iVolumeIndex)) { + p.addWeight(iRayIndex, iVolumeIndex, I); + 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) { + I = (offset - S) * invTminSTimesLengthPerRow; + + iVolumeIndex = row * colCount + col; + // POLICY: PIXEL PRIOR + ADD + POSTERIOR + if (p.pixelPrior(iVolumeIndex)) { + p.addWeight(iRayIndex, iVolumeIndex, lengthPerRow-I); + p.pixelPosterior(iVolumeIndex); + } + + iVolumeIndex++; + // 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()) { - 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); } } + } } // 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->getWindowMaxY() - P) * inv_pixelLengthY; + else { - // get coords - int nextx1 = int((x > 0.0f) ? x : x-1.0f); - float nextx2 = x - nextx1; + // calculate y for col 0 + y = detY + (proj->fRayY/proj->fRayX)*(m_pVolumeGeometry->pixelColToCenterX(0)-detX); + r = (m_pVolumeGeometry->getWindowMaxY() - y) * inv_pixelLengthY - 0.5f; // for each col - for (col = 0; col < m_pVolumeGeometry->getGridColCount(); ++col) { + for (col = 0; col < colCount; ++col, r += update_r) { - x1 = nextx1; - x2 = nextx2; + int row = int(r+0.5f); + offset = r - float32(row); - nextx2 += updatePerCol; - while (nextx2 >= 1.0f) { - nextx2 -= 1.0f; - nextx1++; - } - while (nextx2 < 0.0f) { - nextx2 += 1.0f; - nextx1--; - } - - if (x1 < -1 || x1 > m_pVolumeGeometry->getGridRowCount()) continue; + if (row <= 0 || row >= rowCount-1) continue; // 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); - } + if (offset < -S) { + I = (offset + T) * invTminSTimesLengthPerCol; + + iVolumeIndex = (row-1) * colCount + 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); - } + iVolumeIndex += colCount; + // POLICY: PIXEL PRIOR + ADD + POSTERIOR + if (p.pixelPrior(iVolumeIndex)) { + p.addWeight(iRayIndex, iVolumeIndex, I); + p.pixelPosterior(iVolumeIndex); } } - // 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); - } - } - } - // down - else { - I = (1.5f - T - x2) / (1.0f - 2.0f*T) * lengthPerCol; - - if (x1 >= 0 && x1 < m_pVolumeGeometry->getGridColCount()) { - iVolumeIndex = m_pVolumeGeometry->pixelRowColToIndex(x1, col); - // POLICY: PIXEL PRIOR + ADD + POSTERIOR - if (p.pixelPrior(iVolumeIndex)) { - p.addWeight(iRayIndex, iVolumeIndex, I); - p.pixelPosterior(iVolumeIndex); - } + else if (S < offset) { + I = (offset - S) * invTminSTimesLengthPerCol; + + iVolumeIndex = row * colCount + col; + // POLICY: PIXEL PRIOR + ADD + POSTERIOR + if (p.pixelPrior(iVolumeIndex)) { + p.addWeight(iRayIndex, iVolumeIndex, lengthPerCol-I); + p.pixelPosterior(iVolumeIndex); + } + + iVolumeIndex += colCount; + // 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()) { - 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: PIXEL PRIOR + ADD + POSTERIOR + if (p.pixelPrior(iVolumeIndex)) { + p.addWeight(iRayIndex, iVolumeIndex, lengthPerCol); + p.pixelPosterior(iVolumeIndex); } } + } - } // end loop col + } // POLICY: RAY POSTERIOR p.rayPosterior(iRayIndex); @@ -286,4 +246,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..9f898ce 100644 --- a/include/astra/ParallelBeamLinearKernelProjector2D.inl +++ b/include/astra/ParallelBeamLinearKernelProjector2D.inl @@ -26,12 +26,11 @@ along with the ASTRA Toolbox. If not, see <http://www.gnu.org/licenses/>. $Id$ */ - 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 +47,47 @@ void CParallelBeamLinearKernelProjector2D::projectSingleRay(int _iProjection, in _iDetector, _iDetector + 1, p); } + + //---------------------------------------------------------------------------------------- -// PROJECT BLOCK +// PROJECT BLOCK - vector projection geometry 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; + float32 detX, detY, x, y, c, r, update_c, update_r, offset; + float32 lengthPerRow, lengthPerCol, inv_pixelLengthX, inv_pixelLengthY; + int iVolumeIndex, iRayIndex, row, col, iAngle, iDetector, colCount, rowCount, detCount; + const SParProjection * proj = 0; + + // 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 + inv_pixelLengthX = 1.0f / m_pVolumeGeometry->getPixelLengthX(); + inv_pixelLengthY = 1.0f / m_pVolumeGeometry->getPixelLengthY(); + colCount = m_pVolumeGeometry->getGridColCount(); + rowCount = m_pVolumeGeometry->getGridRowCount(); + 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; - } - - // 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(); + proj = &pVecProjectionGeometry->getProjectionVectors()[iAngle]; - // precalculate kernel limits - lengthPerCol = m_pVolumeGeometry->getPixelLengthX() * inv_sin_theta; - updatePerCol = cos_theta * inv_sin_theta; - inv_pixelLengthY = 1.0f / m_pVolumeGeometry->getPixelLengthY(); + bool vertical = fabs(proj->fRayX) < fabs(proj->fRayY); + if (vertical) { + lengthPerRow = m_pVolumeGeometry->getPixelLengthX() * sqrt(proj->fRayY*proj->fRayY + proj->fRayX*proj->fRayX) / abs(proj->fRayY); + update_c = -m_pVolumeGeometry->getPixelLengthY() * (proj->fRayX/proj->fRayY) * inv_pixelLengthX; + } else { + lengthPerCol = m_pVolumeGeometry->getPixelLengthY() * sqrt(proj->fRayY*proj->fRayY + proj->fRayX*proj->fRayX) / abs(proj->fRayX); + update_r = -m_pVolumeGeometry->getPixelLengthX() * (proj->fRayY/proj->fRayX) * inv_pixelLengthY; + } // loop detectors for (iDetector = _iDetFrom; iDetector < _iDetTo; ++iDetector) { @@ -98,79 +97,70 @@ 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; - } + detX = proj->fDetSX + (iDetector+0.5f) * proj->fDetUX; + detY = proj->fDetSY + (iDetector+0.5f) * proj->fDetUY; // vertically - if (theta <= PIdiv4) { - + if (vertical) { + // calculate x for row 0 - P = (t - sin_theta * m_pVolumeGeometry->pixelRowToCenterY(0)) * inv_cos_theta; - x = m_pVolumeGeometry->coordXToColF(P) - 0.5f; + x = detX + (proj->fRayX/proj->fRayY)*(m_pVolumeGeometry->pixelRowToCenterY(0)-detY); + c = (x - m_pVolumeGeometry->getWindowMinX()) * inv_pixelLengthX - 0.5f; // 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; - - // 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); - } + for (row = 0; row < rowCount; ++row, c += update_c) { + + col = int(c); + offset = c - float32(col); + + if (col <= 0 || col >= colCount-1) continue; + + iVolumeIndex = row * colCount + col; + // POLICY: PIXEL PRIOR + ADD + POSTERIOR + if (p.pixelPrior(iVolumeIndex)) { + p.addWeight(iRayIndex, iVolumeIndex, (1.0f - offset) * 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: PIXEL PRIOR + ADD + POSTERIOR + if (p.pixelPrior(iVolumeIndex)) { + p.addWeight(iRayIndex, iVolumeIndex, offset * lengthPerRow); + p.pixelPosterior(iVolumeIndex); } + } } // 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->coordYToRowF(P) - 0.5f; + // calculate y for col 0 + y = detY + (proj->fRayY/proj->fRayX)*(m_pVolumeGeometry->pixelColToCenterX(0)-detX); + r = (m_pVolumeGeometry->getWindowMaxY() - y) * inv_pixelLengthY - 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); - } + // for each col + for (col = 0; col < colCount; ++col, r += update_r) { + + int row = int(r); + offset = r - float32(row); + + if (row <= 0 || row >= rowCount-1) continue; + + iVolumeIndex = row * colCount + col; + // POLICY: PIXEL PRIOR + ADD + POSTERIOR + if (p.pixelPrior(iVolumeIndex)) { + p.addWeight(iRayIndex, iVolumeIndex, (1.0f - offset) * 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); - } + + iVolumeIndex += colCount; + // POLICY: PIXEL PRIOR + ADD + POSTERIOR + if (p.pixelPrior(iVolumeIndex)) { + p.addWeight(iRayIndex, iVolumeIndex, offset * lengthPerCol); + p.pixelPosterior(iVolumeIndex); } + } } @@ -179,6 +169,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..31d222a 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> @@ -52,247 +52,177 @@ void CParallelBeamStripKernelProjector2D::projectSingleRay(int _iProjection, int 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; + // variables + float32 detLX, detLY, detRX, detRY, S, T, update_c, update_r, offsetL, offsetR, invTminS; + float32 inv_pixelLengthX, inv_pixelLengthY, pixelArea, res, fRxOverRy, fRyOverRx; + int iVolumeIndex, iRayIndex, iAngle, iDetector; + int row, row_top, row_bottom, col, col_left, col_right, colCount, rowCount, detCount; + const SParProjection * proj = 0; - 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 + inv_pixelLengthX = 1.0f / m_pVolumeGeometry->getPixelLengthX(); + inv_pixelLengthY = 1.0f / m_pVolumeGeometry->getPixelLengthY(); + pixelArea = m_pVolumeGeometry->getPixelLengthX() * m_pVolumeGeometry->getPixelLengthY(); + colCount = m_pVolumeGeometry->getGridColCount(); + rowCount = m_pVolumeGeometry->getGridRowCount(); + 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; + proj = &pVecProjectionGeometry->getProjectionVectors()[iAngle]; - // POLICY: RAY PRIOR - if (!p.rayPrior(iRayIndex)) continue; + bool vertical = fabs(proj->fRayX) < fabs(proj->fRayY); + if (vertical) { + fRxOverRy = proj->fRayX/proj->fRayY; + update_c = -m_pVolumeGeometry->getPixelLengthY() * fRxOverRy * inv_pixelLengthX; + S = 0.5f - 0.5f*fabs(fRxOverRy); + T = 0.5f + 0.5f*fabs(fRxOverRy); + invTminS = 1.0f / (T-S); + } else { + fRyOverRx = proj->fRayY/proj->fRayX; + update_r = -m_pVolumeGeometry->getPixelLengthX() * fRyOverRx * inv_pixelLengthY; + S = 0.5f - 0.5f*fabs(fRyOverRx); + T = 0.5f + 0.5f*fabs(fRyOverRx); + invTminS = 1.0f / (T-S); + } - // 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 - 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; + iRayIndex = iAngle * detCount + iDetector; + + // POLICY: RAY PRIOR + if (!p.rayPrior(iRayIndex)) continue; + detLX = proj->fDetSX + iDetector * proj->fDetUX; + detLY = proj->fDetSY + iDetector * proj->fDetUY; + detRX = detLX + proj->fDetUX; + detRY = detLY + proj->fDetUY; + + // vertically + if (vertical) { + + // calculate cL and cR for row 0 + float32 xL = detLX + fRxOverRy*(m_pVolumeGeometry->pixelRowToCenterY(0)-detLY); + float32 cL = (xL - m_pVolumeGeometry->getWindowMinX()) * inv_pixelLengthX - 0.5f; + float32 xR = detRX + fRxOverRy*(m_pVolumeGeometry->pixelRowToCenterY(0)-detRY); + float32 cR = (xR - m_pVolumeGeometry->getWindowMinX()) * inv_pixelLengthX - 0.5f; + + if (cR < cL) { + float32 tmp = cL; + cL = cR; + cR = tmp; + } + // 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 - } else { + for (row = 0; row < rowCount; ++row, cL += update_c, cR += update_c) { - // 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; + col_left = int(cL-0.5f+S); + col_right = int(cR+1.5-S); - updateX = cos_theta * inv_sin_theta; + if (col_left < 0) col_left = 0; + if (col_right > colCount-1) col_right = colCount-1; - // loop detectors - for (iDetector = _iDetFrom; iDetector < _iDetTo; ++iDetector) { - - iRayIndex = iAngle * m_pProjectionGeometry->getDetectorCount() + iDetector; + offsetL = cL - float32(col_left); + offsetR = cR - float32(col_left); - // POLICY: RAY PRIOR - if (!p.rayPrior(iRayIndex)) continue; + // for each column + for (col = col_left; col <= col_right; ++col, offsetL -= 1.0f, offsetR -= 1.0f) { - // get t - t = m_pProjectionGeometry->indexToDetectorOffset(iDetector); - if (switch_t) t = -t; - - // 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; + iVolumeIndex = row * colCount + col; - // 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; + // POLICY: PIXEL PRIOR + ADD + POSTERIOR + if (p.pixelPrior(iVolumeIndex)) { - // for each col - for (col = 0; col < m_pVolumeGeometry->getGridColCount(); ++col) { + // 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; - // get strip extremes in column indices - x1L = int((XLimitL > 0.0f) ? XLimitL : XLimitL-1.0f); - x1R = int((XLimitR > 0.0f) ? XLimitR : XLimitR-1.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; - // 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; + p.addWeight(iRayIndex, iVolumeIndex, pixelArea*res); + p.pixelPosterior(iVolumeIndex); + } + } + } + } - // for each affected col - for (row = x1L; row <= x1R; ++row) { + // horizontally + else { - if (row < 0 || row >= m_pVolumeGeometry->getGridRowCount()) { x2L -= 1.0f; x2R -= 1.0f; continue; } + // calculate rL and rR for row 0 + float32 yL = detLY + fRyOverRx*(m_pVolumeGeometry->pixelColToCenterX(0)-detLX); + float32 rL = (m_pVolumeGeometry->getWindowMaxY() - yL) * inv_pixelLengthY - 0.5f; + float32 yR = detRY + fRyOverRx*(m_pVolumeGeometry->pixelColToCenterX(0)-detRX); + float32 rR = (m_pVolumeGeometry->getWindowMaxY() - yR) * inv_pixelLengthY - 0.5f; - iVolumeIndex = m_pVolumeGeometry->pixelRowColToIndex(row, col); + if (rR < rL) { + float32 tmp = rL; + rL = rR; + rR = tmp; + } - // POLICY: PIXEL PRIOR - if (!p.pixelPrior(iVolumeIndex)) { x2L -= 1.0f; x2R -= 1.0f; continue; } + // for each column + for (col = 0; col < colCount; ++col, rL += update_r, rR += update_r) { - // 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; } + row_top = int(rL-0.5f+S); + row_bottom = int(rR+1.5-S); - // POLICY: ADD - p.addWeight(iRayIndex, iVolumeIndex, PW*PH * res); + if (row_top < 0) row_top = 0; + if (row_bottom > rowCount-1) row_bottom = rowCount-1; - // POLICY: PIXEL POSTERIOR - p.pixelPosterior(iVolumeIndex); + offsetL = rL - float32(row_top); + offsetR = rR - float32(row_top); - x2L -= 1.0f; - x2R -= 1.0f; + // for each row + for (row = row_top; row <= row_bottom; ++row, offsetL -= 1.0f, offsetR -= 1.0f) { - } // end row loop + iVolumeIndex = row * colCount + col; - } // end col loop + // POLICY: PIXEL PRIOR + ADD + POSTERIOR + if (p.pixelPrior(iVolumeIndex)) { - // POLICY: RAY POSTERIOR - p.rayPosterior(iRayIndex); + // 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; - } // end detector loop + // 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; - } // end theta switch + 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/mex/astra_mex_data2d_c.cpp b/matlab/mex/astra_mex_data2d_c.cpp index 5f79e98..62c5d80 100644 --- a/matlab/mex/astra_mex_data2d_c.cpp +++ b/matlab/mex/astra_mex_data2d_c.cpp @@ -44,6 +44,7 @@ $Id$ #include "astra/SparseMatrixProjectionGeometry2D.h" #include "astra/FanFlatProjectionGeometry2D.h" #include "astra/FanFlatVecProjectionGeometry2D.h" +#include "astra/ParallelVecProjectionGeometry2D.h" using namespace std; using namespace astra; @@ -159,6 +160,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(); } @@ -448,6 +451,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 434546a..5a81844 100644 --- a/python/astra/PyIncludes.pxd +++ b/python/astra/PyIncludes.pxd @@ -40,7 +40,6 @@ cdef extern from "astra/Globals.h" namespace "astra": cdef extern from "astra/Config.h" namespace "astra": cdef cppclass Config: Config() - void initialize(string rootname) XMLNode *self cdef extern from "astra/VolumeGeometry2D.h" namespace "astra": @@ -59,7 +58,6 @@ cdef extern from "astra/VolumeGeometry2D.h" namespace "astra": float32 getWindowMinY() float32 getWindowMaxX() float32 getWindowMaxY() - Config* getConfiguration() cdef extern from "astra/Float32VolumeData2D.h" namespace "astra": @@ -69,7 +67,6 @@ cdef extern from "astra/Float32VolumeData2D.h" namespace "astra": int getWidth() int getHeight() void changeGeometry(CVolumeGeometry2D*) - Config* getConfiguration() @@ -82,18 +79,10 @@ cdef extern from "astra/ProjectionGeometry2D.h" namespace "astra": bool isOfType(string) float32 getProjectionAngle(int) float32 getDetectorWidth() - Config* getConfiguration() cdef extern from "astra/Float32Data2D.h" namespace "astra::CFloat32Data2D": - cdef enum TWOEDataType "astra::CFloat32Data2D::EDataType": - TWOPROJECTION "astra::CFloat32Data2D::PROJECTION" - TWOVOLUME "astra::CFloat32Data2D::VOLUME" - -cdef extern from "astra/Float32Data3D.h" namespace "astra::CFloat32Data3D": - cdef enum THREEEDataType "astra::CFloat32Data3D::EDataType": - THREEPROJECTION "astra::CFloat32Data3D::PROJECTION" - THREEVOLUME "astra::CFloat32Data3D::VOLUME" - + cdef enum EDataType: + BASE,PROJECTION,VOLUME cdef extern from "astra/Float32Data2D.h" namespace "astra": cdef cppclass CFloat32Data2D: @@ -103,7 +92,7 @@ cdef extern from "astra/Float32Data2D.h" namespace "astra": float32 **getData2D() int getWidth() int getHeight() - TWOEDataType getType() + EDataType getType() @@ -115,11 +104,15 @@ cdef extern from "astra/SparseMatrixProjectionGeometry2D.h" namespace "astra": cdef extern from "astra/FanFlatProjectionGeometry2D.h" namespace "astra": cdef cppclass CFanFlatProjectionGeometry2D: CFanFlatProjectionGeometry2D() - + 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() @@ -160,8 +153,8 @@ cdef extern from "astra/SparseMatrix.h" namespace "astra": float32* m_pfValues unsigned int* m_piColIndices unsigned long* m_plRowStarts - -cdef extern from "astra/Float32Data3DMemory.h" namespace "astra": + +cdef extern from "astra/Float32Data3DMemory.h" namespace "astra": cdef cppclass CFloat32Data3DMemory: CFloat32Data3DMemory() bool isInitialized() @@ -172,26 +165,22 @@ cdef extern from "astra/Float32Data3DMemory.h" namespace "astra": void updateStatistics() float32 *getData() float32 ***getData3D() - THREEEDataType getType() cdef extern from "astra/VolumeGeometry3D.h" namespace "astra": cdef cppclass CVolumeGeometry3D: CVolumeGeometry3D() bool initialize(Config) - Config * getConfiguration() cdef extern from "astra/ProjectionGeometry3D.h" namespace "astra": cdef cppclass CProjectionGeometry3D: CProjectionGeometry3D() bool initialize(Config) - Config * getConfiguration() - - + + cdef extern from "astra/Float32VolumeData3DMemory.h" namespace "astra": cdef cppclass CFloat32VolumeData3DMemory: CFloat32VolumeData3DMemory(CVolumeGeometry3D*) - CVolumeGeometry3D* getGeometry() cdef extern from "astra/ParallelProjectionGeometry3D.h" namespace "astra": @@ -206,7 +195,7 @@ cdef extern from "astra/ConeProjectionGeometry3D.h" namespace "astra": cdef cppclass CConeProjectionGeometry3D: CConeProjectionGeometry3D() bool initialize(Config) - + cdef extern from "astra/ConeVecProjectionGeometry3D.h" namespace "astra": cdef cppclass CConeVecProjectionGeometry3D: CConeVecProjectionGeometry3D() @@ -214,10 +203,9 @@ cdef extern from "astra/ConeVecProjectionGeometry3D.h" namespace "astra": cdef extern from "astra/Float32ProjectionData3DMemory.h" namespace "astra": cdef cppclass CFloat32ProjectionData3DMemory: CFloat32ProjectionData3DMemory(CProjectionGeometry3D*) - CFloat32ProjectionData3DMemory(CConeProjectionGeometry3D*) - CProjectionGeometry3D* getGeometry() + CFloat32ProjectionData3DMemory(CConeProjectionGeometry3D*) -cdef extern from "astra/Float32Data3D.h" namespace "astra": +cdef extern from "astra/Float32Data3D.h" namespace "astra": cdef cppclass CFloat32Data3D: CFloat32Data3D() bool isInitialized() @@ -226,3 +214,6 @@ cdef extern from "astra/Float32Data3D.h" namespace "astra": int getHeight() int getDepth() void updateStatistics() + + + diff --git a/python/astra/algorithm_c.pyx b/python/astra/algorithm_c.pyx index 966d3d7..0c2999c 100644 --- a/python/astra/algorithm_c.pyx +++ b/python/astra/algorithm_c.pyx @@ -49,17 +49,19 @@ cdef extern from *: def create(config): - cdef Config * cfg = utils.dictToConfig(six.b('Algorithm'), config) + cdef XMLDocument * xml = utils.dict2XML(six.b('Algorithm'), config) + cdef Config cfg cdef CAlgorithm * alg + cfg.self = xml.getRootNode() alg = PyAlgorithmFactory.getSingletonPtr().create(cfg.self.getAttribute(six.b('type'))) if alg == NULL: - del cfg + del xml raise Exception("Unknown algorithm.") - if not alg.initialize(cfg[0]): - del cfg + if not alg.initialize(cfg): + del xml del alg raise Exception("Algorithm not initialized.") - del cfg + del xml return manAlg.store(alg) cdef CAlgorithm * getAlg(i) except NULL: diff --git a/python/astra/creators.py b/python/astra/creators.py index 9aba464..b5ecd5a 100644 --- a/python/astra/creators.py +++ b/python/astra/creators.py @@ -139,6 +139,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, source_det)``: @@ -226,6 +233,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, source_det)') diff --git a/python/astra/data2d_c.pyx b/python/astra/data2d_c.pyx index b9c105e..dbe1c2f 100644 --- a/python/astra/data2d_c.pyx +++ b/python/astra/data2d_c.pyx @@ -47,8 +47,10 @@ from .PyIncludes cimport * cimport utils from .utils import wrap_from_bytes + cdef CData2DManager * man2d = <CData2DManager * >PyData2DManager.getSingletonPtr() + def clear(): man2d.clear() @@ -62,22 +64,25 @@ def delete(ids): def create(datatype, geometry, data=None): - cdef Config *cfg + cdef XMLDocument * xml + cdef Config cfg cdef CVolumeGeometry2D * pGeometry cdef CProjectionGeometry2D * ppGeometry cdef CFloat32Data2D * pDataObject2D if datatype == '-vol': - cfg = utils.dictToConfig(six.b('VolumeGeometry'), geometry) + xml = utils.dict2XML(six.b('VolumeGeometry'), geometry) + cfg.self = xml.getRootNode() pGeometry = new CVolumeGeometry2D() - if not pGeometry.initialize(cfg[0]): - del cfg + if not pGeometry.initialize(cfg): + del xml del pGeometry raise Exception('Geometry class not initialized.') pDataObject2D = <CFloat32Data2D * > new CFloat32VolumeData2D(pGeometry) - del cfg + del xml del pGeometry elif datatype == '-sino': - cfg = utils.dictToConfig(six.b('ProjectionGeometry'), geometry) + xml = utils.dict2XML(six.b('ProjectionGeometry'), geometry) + cfg.self = xml.getRootNode() tpe = wrap_from_bytes(cfg.self.getAttribute(six.b('type'))) if (tpe == 'sparse_matrix'): ppGeometry = <CProjectionGeometry2D * >new CSparseMatrixProjectionGeometry2D() @@ -85,15 +90,17 @@ def create(datatype, geometry, data=None): 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]): - del cfg + if not ppGeometry.initialize(cfg): + del xml del ppGeometry raise Exception('Geometry class not initialized.') pDataObject2D = <CFloat32Data2D * > new CFloat32ProjectionData2D(ppGeometry) del ppGeometry - del cfg + del xml else: raise Exception("Invalid datatype. Please specify '-vol' or '-sino'.") @@ -146,27 +153,29 @@ def get_geometry(i): cdef CFloat32Data2D * pDataObject = getObject(i) cdef CFloat32ProjectionData2D * pDataObject2 cdef CFloat32VolumeData2D * pDataObject3 - if pDataObject.getType() == TWOPROJECTION: + if pDataObject.getType() == PROJECTION: pDataObject2 = <CFloat32ProjectionData2D * >pDataObject - geom = utils.configToDict(pDataObject2.getGeometry().getConfiguration()) - elif pDataObject.getType() == TWOVOLUME: + geom = utils.createProjectionGeometryStruct(pDataObject2.getGeometry()) + elif pDataObject.getType() == VOLUME: pDataObject3 = <CFloat32VolumeData2D * >pDataObject - geom = utils.configToDict(pDataObject3.getGeometry().getConfiguration()) + geom = utils.createVolumeGeometryStruct(pDataObject3.getGeometry()) else: raise Exception("Not a known data object") return geom def change_geometry(i, geom): - cdef Config *cfg + cdef XMLDocument * xml + cdef Config cfg cdef CVolumeGeometry2D * pGeometry cdef CProjectionGeometry2D * ppGeometry cdef CFloat32Data2D * pDataObject = getObject(i) cdef CFloat32ProjectionData2D * pDataObject2 cdef CFloat32VolumeData2D * pDataObject3 - if pDataObject.getType() == TWOPROJECTION: + if pDataObject.getType() == PROJECTION: pDataObject2 = <CFloat32ProjectionData2D * >pDataObject - cfg = utils.dictToConfig(six.b('ProjectionGeometry'), geom) + xml = utils.dict2XML(six.b('ProjectionGeometry'), geom) + cfg.self = xml.getRootNode() tpe = wrap_from_bytes(cfg.self.getAttribute(six.b('type'))) if (tpe == 'sparse_matrix'): ppGeometry = <CProjectionGeometry2D * >new CSparseMatrixProjectionGeometry2D() @@ -174,35 +183,38 @@ 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]): - del cfg + if not ppGeometry.initialize(cfg): + del xml del ppGeometry raise Exception('Geometry class not initialized.') if (ppGeometry.getDetectorCount() != pDataObject2.getDetectorCount() or ppGeometry.getProjectionAngleCount() != pDataObject2.getAngleCount()): del ppGeometry - del cfg + del xml raise Exception( "The dimensions of the data do not match those specified in the geometry.") pDataObject2.changeGeometry(ppGeometry) del ppGeometry - del cfg - elif pDataObject.getType() == TWOVOLUME: + del xml + elif pDataObject.getType() == VOLUME: pDataObject3 = <CFloat32VolumeData2D * >pDataObject - cfg = utils.dictToConfig(six.b('VolumeGeometry'), geom) + xml = utils.dict2XML(six.b('VolumeGeometry'), geom) + cfg.self = xml.getRootNode() pGeometry = new CVolumeGeometry2D() - if not pGeometry.initialize(cfg[0]): - del cfg + if not pGeometry.initialize(cfg): + del xml del pGeometry raise Exception('Geometry class not initialized.') if (pGeometry.getGridColCount() != pDataObject3.getWidth() or pGeometry.getGridRowCount() != pDataObject3.getHeight()): - del cfg + del xml del pGeometry raise Exception( 'The dimensions of the data do not match those specified in the geometry.') pDataObject3.changeGeometry(pGeometry) - del cfg + del xml del pGeometry else: raise Exception("Not a known data object") diff --git a/python/astra/data3d.py b/python/astra/data3d.py index a2e9201..33bde51 100644 --- a/python/astra/data3d.py +++ b/python/astra/data3d.py @@ -27,7 +27,7 @@ from . import data3d_c as d def create(datatype,geometry,data=None): """Create a 3D object. - + :param datatype: Data object type, '-vol' or '-sino'. :type datatype: :class:`string` :param geometry: Volume or projection geometry. @@ -35,77 +35,67 @@ def create(datatype,geometry,data=None): :param data: Data to fill the constructed object with, either a scalar or array. :type data: :class:`float` or :class:`numpy.ndarray` :returns: :class:`int` -- the ID of the constructed object. - + """ return d.create(datatype,geometry,data) def get(i): """Get a 3D object. - + :param i: ID of object to get. :type i: :class:`int` :returns: :class:`numpy.ndarray` -- The object data. - + """ return d.get(i) def get_shared(i): """Get a 3D object with memory shared between the ASTRA toolbox and numpy array. - + :param i: ID of object to get. :type i: :class:`int` :returns: :class:`numpy.ndarray` -- The object data. - + """ return d.get_shared(i) def get_single(i): """Get a 3D object in single precision. - + :param i: ID of object to get. :type i: :class:`int` :returns: :class:`numpy.ndarray` -- The object data. - + """ return g.get_single(i) def store(i,data): """Fill existing 3D object with data. - + :param i: ID of object to fill. :type i: :class:`int` :param data: Data to fill the object with, either a scalar or array. :type data: :class:`float` or :class:`numpy.ndarray` - + """ return d.store(i,data) -def get_geometry(i): - """Get the geometry of a 3D object. - - :param i: ID of object. - :type i: :class:`int` - :returns: :class:`dict` -- The geometry of object with ID ``i``. - - """ - return d.get_geometry(i) - def dimensions(i): """Get dimensions of a 3D object. - + :param i: ID of object. :type i: :class:`int` :returns: :class:`tuple` -- dimensions of object with ID ``i``. - + """ return d.dimensions(i) def delete(ids): """Delete a 2D object. - + :param ids: ID or list of ID's to delete. :type ids: :class:`int` or :class:`list` - + """ return d.delete(ids) diff --git a/python/astra/data3d_c.pyx b/python/astra/data3d_c.pyx index 4b069f7..f821aaf 100644 --- a/python/astra/data3d_c.pyx +++ b/python/astra/data3d_c.pyx @@ -51,23 +51,26 @@ cdef extern from *: CFloat32Data3DMemory * dynamic_cast_mem "dynamic_cast<astra::CFloat32Data3DMemory*>" (CFloat32Data3D * ) except NULL def create(datatype,geometry,data=None): - cdef Config *cfg + cdef XMLDocument * xml + cdef Config cfg cdef CVolumeGeometry3D * pGeometry cdef CProjectionGeometry3D * ppGeometry cdef CFloat32Data3DMemory * pDataObject3D cdef CConeProjectionGeometry3D* pppGeometry if datatype == '-vol': - cfg = utils.dictToConfig(six.b('VolumeGeometry'), geometry) + xml = utils.dict2XML(six.b('VolumeGeometry'), geometry) + cfg.self = xml.getRootNode() pGeometry = new CVolumeGeometry3D() - if not pGeometry.initialize(cfg[0]): - del cfg + if not pGeometry.initialize(cfg): + del xml del pGeometry raise Exception('Geometry class not initialized.') pDataObject3D = <CFloat32Data3DMemory * > new CFloat32VolumeData3DMemory(pGeometry) - del cfg + del xml del pGeometry elif datatype == '-sino' or datatype == '-proj3d': - cfg = utils.dictToConfig(six.b('ProjectionGeometry'), geometry) + xml = utils.dict2XML(six.b('ProjectionGeometry'), geometry) + cfg.self = xml.getRootNode() tpe = wrap_from_bytes(cfg.self.getAttribute(six.b('type'))) if (tpe == "parallel3d"): ppGeometry = <CProjectionGeometry3D*> new CParallelProjectionGeometry3D(); @@ -80,18 +83,19 @@ def create(datatype,geometry,data=None): else: raise Exception("Invalid geometry type.") - if not ppGeometry.initialize(cfg[0]): - del cfg + if not ppGeometry.initialize(cfg): + del xml del ppGeometry raise Exception('Geometry class not initialized.') pDataObject3D = <CFloat32Data3DMemory * > new CFloat32ProjectionData3DMemory(ppGeometry) del ppGeometry - del cfg + del xml elif datatype == "-sinocone": - cfg = utils.dictToConfig(six.b('ProjectionGeometry'), geometry) + xml = utils.dict2XML(six.b('ProjectionGeometry'), geometry) + cfg.self = xml.getRootNode() pppGeometry = new CConeProjectionGeometry3D() - if not pppGeometry.initialize(cfg[0]): - del cfg + if not pppGeometry.initialize(cfg): + del xml del pppGeometry raise Exception('Geometry class not initialized.') pDataObject3D = <CFloat32Data3DMemory * > new CFloat32ProjectionData3DMemory(pppGeometry) @@ -108,19 +112,6 @@ def create(datatype,geometry,data=None): return man3d.store(<CFloat32Data3D*>pDataObject3D) -def get_geometry(i): - cdef CFloat32Data3DMemory * pDataObject = dynamic_cast_mem(getObject(i)) - cdef CFloat32ProjectionData3DMemory * pDataObject2 - cdef CFloat32VolumeData3DMemory * pDataObject3 - if pDataObject.getType() == THREEPROJECTION: - pDataObject2 = <CFloat32ProjectionData3DMemory * >pDataObject - geom = utils.configToDict(pDataObject2.getGeometry().getConfiguration()) - elif pDataObject.getType() == THREEVOLUME: - pDataObject3 = <CFloat32VolumeData3DMemory * >pDataObject - geom = utils.configToDict(pDataObject3.getGeometry().getConfiguration()) - else: - raise Exception("Not a known data object") - return geom cdef fillDataObject(CFloat32Data3DMemory * obj, data): if data is None: diff --git a/python/astra/functions.py b/python/astra/functions.py index 4025468..bbbb355 100644 --- a/python/astra/functions.py +++ b/python/astra/functions.py @@ -171,7 +171,7 @@ def geom_size(geom, dim=None): elif geom['type'] == 'parallel3d' or geom['type'] == 'cone': s = (geom['DetectorRowCount'], len( geom['ProjectionAngles']), geom['DetectorColCount']) - elif geom['type'] == 'fanflat_vec': + elif geom['type'] == 'parallel_vec' or geom['type'] == 'fanflat_vec': s = (geom['Vectors'].shape[0], geom['DetectorCount']) elif geom['type'] == 'parallel3d_vec' or geom['type'] == 'cone_vec': s = (geom['DetectorRowCount'], geom[ @@ -189,7 +189,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)): @@ -268,3 +288,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/python/astra/projector_c.pyx b/python/astra/projector_c.pyx index f91a8dd..978ca09 100644 --- a/python/astra/projector_c.pyx +++ b/python/astra/projector_c.pyx @@ -49,13 +49,15 @@ cdef CMatrixManager * manM = <CMatrixManager * >PyMatrixManager.getSingletonPtr( def create(config): - cdef Config * cfg = utils.dictToConfig(six.b('Projector2D'), config) + cdef XMLDocument * xml = utils.dict2XML(six.b('Projector2D'), config) + cdef Config cfg cdef CProjector2D * proj - proj = PyProjector2DFactory.getSingletonPtr().create(cfg[0]) + cfg.self = xml.getRootNode() + proj = PyProjector2DFactory.getSingletonPtr().create(cfg) if proj == NULL: - del cfg + del xml raise Exception("Error creating projector.") - del cfg + del xml return manProj.store(proj) @@ -85,12 +87,12 @@ cdef CProjector2D * getObject(i) except NULL: def projection_geometry(i): cdef CProjector2D * proj = getObject(i) - return utils.configToDict(proj.getProjectionGeometry().getConfiguration()) + return utils.createProjectionGeometryStruct(proj.getProjectionGeometry()) def volume_geometry(i): cdef CProjector2D * proj = getObject(i) - return utils.configToDict(proj.getVolumeGeometry().getConfiguration()) + return utils.createVolumeGeometryStruct(proj.getVolumeGeometry()) def weights_single_ray(i, projection_index, detector_index): diff --git a/python/astra/utils.pxd b/python/astra/utils.pxd index ca84836..55db9d3 100644 --- a/python/astra/utils.pxd +++ b/python/astra/utils.pxd @@ -31,5 +31,7 @@ from .PyXMLDocument cimport XMLNode from .PyIncludes cimport * -cdef configToDict(Config *) -cdef Config * dictToConfig(string rootname, dc) +cdef XMLDocument *dict2XML(string rootname, dc) +cdef XML2dict(XMLDocument *) +cdef createVolumeGeometryStruct(CVolumeGeometry2D* geom) +cdef createProjectionGeometryStruct(CProjectionGeometry2D* geom) diff --git a/python/astra/utils.pyx b/python/astra/utils.pyx index 0439f1b..0b8d527 100644 --- a/python/astra/utils.pyx +++ b/python/astra/utils.pyx @@ -31,7 +31,7 @@ import six from libcpp.string cimport string from libcpp.list cimport list from libcpp.vector cimport vector -from cython.operator cimport dereference as deref, preincrement as inc +from cython.operator cimport dereference as deref from cpython.version cimport PY_MAJOR_VERSION cimport PyXMLDocument @@ -40,16 +40,18 @@ from .PyXMLDocument cimport XMLNode from .PyIncludes cimport * -cdef Config * dictToConfig(string rootname, dc): - cdef Config * cfg = new Config() - cfg.initialize(rootname) +cdef XMLDocument * dict2XML(string rootname, dc): + cdef XMLDocument * doc = PyXMLDocument.createDocument(rootname) + cdef XMLNode * node = doc.getRootNode() try: - readDict(cfg.self, dc) - except Exception as e: - del cfg - six.print_(e.strerror) - return NULL - return cfg + readDict(node, dc) + except: + six.print_('Error reading XML') + del doc + doc = NULL + finally: + del node + return doc def convert_item(item): if isinstance(item, six.string_types): @@ -164,73 +166,97 @@ cdef void readOptions(XMLNode * node, dc): else: node.addOption(item, wrap_to_bytes(val)) -cdef configToDict(Config *cfg): - return XMLNode2dict(cfg.self) - -def castString3(input): - return input.decode('utf-8') - -def castString2(input): - return input - -if six.PY3: - castString = castString3 -else: - castString = castString2 - -def stringToPythonValue(inputIn): - input = castString(inputIn) - # matrix - if ';' in input: - row_strings = input.split(';') - col_strings = row_strings[0].split(',') - nRows = len(row_strings) - nCols = len(col_strings) - - out = np.empty((nRows,nCols)) - for ridx, row in enumerate(row_strings): - col_strings = row.split(',') - for cidx, col in enumerate(col_strings): - out[ridx,cidx] = float(col) - return out - - # vector - if ',' in input: - items = input.split(',') - out = np.empty(len(items)) - for idx,item in enumerate(items): - out[idx] = float(item) - return out - - try: - # integer - return int(input) - except ValueError: - try: - #float - return float(input) - except ValueError: - # string - return str(input) - +cdef vectorToNumpy(vector[float32] inp): + cdef int i + cdef int sz = inp.size() + ret = np.empty(sz) + for i in range(sz): + ret[i] = inp[i] + return ret cdef XMLNode2dict(XMLNode * node): cdef XMLNode * subnode cdef list[XMLNode * ] nodes cdef list[XMLNode * ].iterator it dct = {} - opts = {} if node.hasAttribute(six.b('type')): - dct['type'] = castString(node.getAttribute(six.b('type'))) + dct['type'] = node.getAttribute(six.b('type')) nodes = node.getNodes() it = nodes.begin() while it != nodes.end(): subnode = deref(it) - if castString(subnode.getName())=="Option": - opts[castString(subnode.getAttribute('key'))] = stringToPythonValue(subnode.getAttribute('value')) + if subnode.hasAttribute(six.b('listsize')): + dct[subnode.getName( + )] = vectorToNumpy(subnode.getContentNumericalArray()) else: - dct[castString(subnode.getName())] = stringToPythonValue(subnode.getContent()) + dct[subnode.getName()] = subnode.getContent() del subnode - inc(it) - if len(opts)>0: dct['options'] = opts return dct + +cdef XML2dict(XMLDocument * xml): + cdef XMLNode * node = xml.getRootNode() + dct = XMLNode2dict(node) + del node; + return dct; + +cdef createProjectionGeometryStruct(CProjectionGeometry2D * geom): + cdef int i + cdef CFanFlatVecProjectionGeometry2D * fanvecGeom + # cdef SFanProjection* p + dct = {} + dct['DetectorCount'] = geom.getDetectorCount() + if not geom.isOfType(< string > six.b('fanflat_vec')): + dct['DetectorWidth'] = geom.getDetectorWidth() + angles = np.empty(geom.getProjectionAngleCount()) + for i in range(geom.getProjectionAngleCount()): + angles[i] = geom.getProjectionAngle(i) + dct['ProjectionAngles'] = angles + else: + raise Exception("Not yet implemented") + # fanvecGeom = <CFanFlatVecProjectionGeometry2D*> geom + # vecs = np.empty(fanvecGeom.getProjectionAngleCount()*6) + # iDetCount = pVecGeom.getDetectorCount() + # for i in range(fanvecGeom.getProjectionAngleCount()): + # p = &fanvecGeom.getProjectionVectors()[i]; + # out[6*i + 0] = p.fSrcX + # out[6*i + 1] = p.fSrcY + # out[6*i + 2] = p.fDetSX + 0.5f*iDetCount*p.fDetUX + # out[6*i + 3] = p.fDetSY + 0.5f*iDetCount*p.fDetUY + # out[6*i + 4] = p.fDetUX + # out[6*i + 5] = p.fDetUY + # dct['Vectors'] = vecs + if (geom.isOfType(< string > six.b('parallel'))): + dct["type"] = "parallel" + if (geom.isOfType(< string > six.b('parallel_vec'))): + dct["type"] = "parallel_vec" + elif (geom.isOfType(< string > six.b('fanflat'))): + raise Exception("Not yet implemented") + # astra::CFanFlatProjectionGeometry2D* pFanFlatGeom = dynamic_cast<astra::CFanFlatProjectionGeometry2D*>(_pProjGeom) + # mGeometryInfo["DistanceOriginSource"] = mxCreateDoubleScalar(pFanFlatGeom->getOriginSourceDistance()) + # mGeometryInfo["DistanceOriginDetector"] = + # mxCreateDoubleScalar(pFanFlatGeom->getOriginDetectorDistance()) + dct["type"] = "fanflat" + elif (geom.isOfType(< string > six.b('sparse_matrix'))): + raise Exception("Not yet implemented") + # astra::CSparseMatrixProjectionGeometry2D* pSparseMatrixGeom = + # dynamic_cast<astra::CSparseMatrixProjectionGeometry2D*>(_pProjGeom); + dct["type"] = "sparse_matrix" + # dct["MatrixID"] = + # mxCreateDoubleScalar(CMatrixManager::getSingleton().getIndex(pSparseMatrixGeom->getMatrix())) + elif(geom.isOfType(< string > six.b('fanflat_vec'))): + dct["type"] = "fanflat_vec" + return dct + +cdef createVolumeGeometryStruct(CVolumeGeometry2D * geom): + mGeometryInfo = {} + mGeometryInfo["GridColCount"] = geom.getGridColCount() + mGeometryInfo["GridRowCount"] = geom.getGridRowCount() + + mGeometryOptions = {} + mGeometryOptions["WindowMinX"] = geom.getWindowMinX() + mGeometryOptions["WindowMaxX"] = geom.getWindowMaxX() + mGeometryOptions["WindowMinY"] = geom.getWindowMinY() + mGeometryOptions["WindowMaxY"] = geom.getWindowMaxY() + + mGeometryInfo["option"] = mGeometryOptions + return mGeometryInfo diff --git a/src/ConeVecProjectionGeometry3D.cpp b/src/ConeVecProjectionGeometry3D.cpp index 9dc273d..a4bd22d 100644 --- a/src/ConeVecProjectionGeometry3D.cpp +++ b/src/ConeVecProjectionGeometry3D.cpp @@ -208,7 +208,7 @@ Config* CConeVecProjectionGeometry3D::getConfiguration() const Config* cfg = new Config(); cfg->initialize("ProjectionGeometry3D"); - cfg->self->addAttribute("type", "cone_vec"); + cfg->self->addAttribute("type", "cone"); cfg->self->addChildNode("DetectorRowCount", m_iDetectorRowCount); cfg->self->addChildNode("DetectorColCount", m_iDetectorColCount); @@ -218,9 +218,9 @@ Config* CConeVecProjectionGeometry3D::getConfiguration() const vectors += boost::lexical_cast<string>(p.fSrcX) + ","; vectors += boost::lexical_cast<string>(p.fSrcY) + ","; vectors += boost::lexical_cast<string>(p.fSrcZ) + ","; - vectors += boost::lexical_cast<string>(p.fDetSX + 0.5f*m_iDetectorRowCount*p.fDetVX + 0.5f*m_iDetectorColCount*p.fDetUX) + ","; - vectors += boost::lexical_cast<string>(p.fDetSY + 0.5f*m_iDetectorRowCount*p.fDetVY + 0.5f*m_iDetectorColCount*p.fDetUY) + ","; - vectors += boost::lexical_cast<string>(p.fDetSZ + 0.5f*m_iDetectorRowCount*p.fDetVZ + 0.5f*m_iDetectorColCount*p.fDetUZ) + ","; + vectors += boost::lexical_cast<string>(p.fDetSX + 0.5f*m_iDetectorRowCount*p.fDetUX + 0.5f*m_iDetectorColCount*p.fDetVX) + ","; + vectors += boost::lexical_cast<string>(p.fDetSY + 0.5f*m_iDetectorRowCount*p.fDetUY + 0.5f*m_iDetectorColCount*p.fDetVY) + ","; + vectors += boost::lexical_cast<string>(p.fDetSZ + 0.5f*m_iDetectorRowCount*p.fDetUZ + 0.5f*m_iDetectorColCount*p.fDetVZ) + ","; vectors += boost::lexical_cast<string>(p.fDetUX) + ","; vectors += boost::lexical_cast<string>(p.fDetUY) + ","; vectors += boost::lexical_cast<string>(p.fDetUZ) + ","; diff --git a/src/FanFlatProjectionGeometry2D.cpp b/src/FanFlatProjectionGeometry2D.cpp index d757f18..9851d40 100644 --- a/src/FanFlatProjectionGeometry2D.cpp +++ b/src/FanFlatProjectionGeometry2D.cpp @@ -217,7 +217,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 3253f88..d030576 100644 --- a/src/ParallelBeamBlobKernelProjector2D.cpp +++ b/src/ParallelBeamBlobKernelProjector2D.cpp @@ -102,7 +102,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."); @@ -156,16 +156,6 @@ bool CParallelBeamBlobKernelProjector2D::initialize(const Config& _cfg) 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 @@ -228,44 +218,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 5a23413..c94c024 100644 --- a/src/ParallelBeamLineKernelProjector2D.cpp +++ b/src/ParallelBeamLineKernelProjector2D.cpp @@ -88,7 +88,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."); @@ -162,61 +162,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 a710664..043db71 100644 --- a/src/ParallelBeamLinearKernelProjector2D.cpp +++ b/src/ParallelBeamLinearKernelProjector2D.cpp @@ -89,7 +89,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 44c6fec..bb693a2 100644 --- a/src/ParallelBeamStripKernelProjector2D.cpp +++ b/src/ParallelBeamStripKernelProjector2D.cpp @@ -88,7 +88,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."); @@ -164,57 +164,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 cac8f30..42ed0a0 100644 --- a/src/ParallelProjectionGeometry2D.cpp +++ b/src/ParallelProjectionGeometry2D.cpp @@ -67,8 +67,7 @@ CParallelProjectionGeometry2D::CParallelProjectionGeometry2D(const CParallelProj initialize(_projGeom.m_iProjectionAngleCount, _projGeom.m_iDetectorCount, _projGeom.m_fDetectorWidth, - _projGeom.m_pfProjectionAngles, - _projGeom.m_pfExtraDetectorOffset); + _projGeom.m_pfProjectionAngles); } //---------------------------------------------------------------------------------------- @@ -182,8 +181,8 @@ Config* CParallelProjectionGeometry2D::getConfiguration() const cfg->self->addChildNode("ProjectionAngles", m_pfProjectionAngles, m_iProjectionAngleCount); return cfg; } -//---------------------------------------------------------------------------------------- +//---------------------------------------------------------------------------------------- CVector3D CParallelProjectionGeometry2D::getProjectionDirection(int _iProjectionIndex, int _iDetectorIndex /* = 0 */) { CVector3D vOutput; @@ -197,4 +196,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..c1ed1c4 --- /dev/null +++ b/src/ParallelVecProjectionGeometry2D.cpp @@ -0,0 +1,239 @@ +/* +----------------------------------------------------------------------- +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); + + XMLNode* node; + + // TODO: Fix up class hierarchy... this class doesn't fit very well. + // initialization of parent class + //CProjectionGeometry2D::initialize(_cfg); + + // Required: DetectorCount + node = _cfg.self->getSingleNode("DetectorCount"); + ASTRA_CONFIG_CHECK(node, "ParallelVecProjectionGeometry2D", "No DetectorRowCount tag specified."); + m_iDetectorCount = boost::lexical_cast<int>(node->getContent()); + ASTRA_DELETE(node); + 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_DELETE(node); + 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/ParallelVecProjectionGeometry3D.cpp b/src/ParallelVecProjectionGeometry3D.cpp index dc325e9..cfac485 100644 --- a/src/ParallelVecProjectionGeometry3D.cpp +++ b/src/ParallelVecProjectionGeometry3D.cpp @@ -208,7 +208,7 @@ Config* CParallelVecProjectionGeometry3D::getConfiguration() const Config* cfg = new Config(); cfg->initialize("ProjectionGeometry3D"); - cfg->self->addAttribute("type", "parallel3d_vec"); + cfg->self->addAttribute("type", "parallel3d"); cfg->self->addChildNode("DetectorRowCount", m_iDetectorRowCount); cfg->self->addChildNode("DetectorColCount", m_iDetectorColCount); @@ -218,9 +218,9 @@ Config* CParallelVecProjectionGeometry3D::getConfiguration() const vectors += boost::lexical_cast<string>(p.fRayX) + ","; vectors += boost::lexical_cast<string>(p.fRayY) + ","; vectors += boost::lexical_cast<string>(p.fRayZ) + ","; - vectors += boost::lexical_cast<string>(p.fDetSX + 0.5f*m_iDetectorRowCount*p.fDetVX + 0.5f*m_iDetectorColCount*p.fDetUX) + ","; - vectors += boost::lexical_cast<string>(p.fDetSY + 0.5f*m_iDetectorRowCount*p.fDetVY + 0.5f*m_iDetectorColCount*p.fDetUY) + ","; - vectors += boost::lexical_cast<string>(p.fDetSZ + 0.5f*m_iDetectorRowCount*p.fDetVZ + 0.5f*m_iDetectorColCount*p.fDetUZ) + ","; + vectors += boost::lexical_cast<string>(p.fDetSX + 0.5f*m_iDetectorRowCount*p.fDetUX + 0.5f*m_iDetectorColCount*p.fDetVX) + ","; + vectors += boost::lexical_cast<string>(p.fDetSY + 0.5f*m_iDetectorRowCount*p.fDetUY + 0.5f*m_iDetectorColCount*p.fDetVY) + ","; + vectors += boost::lexical_cast<string>(p.fDetSZ + 0.5f*m_iDetectorRowCount*p.fDetUZ + 0.5f*m_iDetectorColCount*p.fDetVZ) + ","; vectors += boost::lexical_cast<string>(p.fDetUX) + ","; vectors += boost::lexical_cast<string>(p.fDetUY) + ","; vectors += boost::lexical_cast<string>(p.fDetUZ) + ","; diff --git a/src/ProjectionGeometry2D.cpp b/src/ProjectionGeometry2D.cpp index 89b5fe0..a51d86b 100644 --- a/src/ProjectionGeometry2D.cpp +++ b/src/ProjectionGeometry2D.cpp @@ -72,7 +72,6 @@ void CProjectionGeometry2D::_clear() m_iDetectorCount = 0; m_fDetectorWidth = 0.0f; m_pfProjectionAngles = NULL; - m_pfExtraDetectorOffset = NULL; m_bInitialized = false; } @@ -85,10 +84,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; } @@ -150,19 +147,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."); @@ -189,10 +173,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 32a2956..b0ace92 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)); |