summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorWillem Jan Palenstijn <Willem.Jan.Palenstijn@cwi.nl>2018-01-09 14:09:40 +0100
committerWillem Jan Palenstijn <Willem.Jan.Palenstijn@cwi.nl>2018-01-09 14:09:40 +0100
commitde27e439a0c59fade175fba4e0b4a1e0c84b933d (patch)
tree8d724e10b35291f5bfd63174eeeca95e52a863df
parent324611ce98a82944def875e61cb93dd98ced9c79 (diff)
parent84da1d5e27abadf28e97695e88494c58bf1c2697 (diff)
downloadastra-de27e439a0c59fade175fba4e0b4a1e0c84b933d.tar.gz
astra-de27e439a0c59fade175fba4e0b4a1e0c84b933d.tar.bz2
astra-de27e439a0c59fade175fba4e0b4a1e0c84b933d.tar.xz
astra-de27e439a0c59fade175fba4e0b4a1e0c84b933d.zip
Merge branch 'parallel_vec'
-rw-r--r--astra_vc14.vcxproj10
-rw-r--r--astra_vc14.vcxproj.filters15
-rw-r--r--build/linux/Makefile.in3
-rw-r--r--build/msvc/gen.py5
-rw-r--r--cuda/2d/algo.cu62
-rw-r--r--cuda/2d/algo.h22
-rw-r--r--cuda/2d/astra.cu731
-rw-r--r--cuda/2d/astra.h155
-rw-r--r--cuda/2d/cgls.cu36
-rw-r--r--cuda/2d/dims.h5
-rw-r--r--cuda/2d/em.cu28
-rw-r--r--cuda/2d/fbp.cu347
-rw-r--r--cuda/2d/fbp.h98
-rw-r--r--cuda/2d/fbp_filters.h4
-rw-r--r--cuda/2d/fft.cu12
-rw-r--r--cuda/2d/fft.h8
-rw-r--r--cuda/2d/par_bp.cu166
-rw-r--r--cuda/2d/par_bp.h6
-rw-r--r--cuda/2d/par_fp.cu358
-rw-r--r--cuda/2d/par_fp.h4
-rw-r--r--cuda/2d/sart.cu8
-rw-r--r--cuda/2d/sirt.cu45
-rw-r--r--cuda/3d/fdk.cu22
-rw-r--r--include/astra/CudaFilteredBackProjectionAlgorithm.h24
-rw-r--r--include/astra/FanFlatBeamLineKernelProjector2D.inl305
-rw-r--r--include/astra/FanFlatProjectionGeometry2D.h5
-rw-r--r--include/astra/GeometryUtil2D.h72
-rw-r--r--include/astra/Globals.h2
-rw-r--r--include/astra/ParallelBeamBlobKernelProjector2D.h7
-rw-r--r--include/astra/ParallelBeamBlobKernelProjector2D.inl310
-rw-r--r--include/astra/ParallelBeamLineKernelProjector2D.h2
-rw-r--r--include/astra/ParallelBeamLineKernelProjector2D.inl390
-rw-r--r--include/astra/ParallelBeamLinearKernelProjector2D.h2
-rw-r--r--include/astra/ParallelBeamLinearKernelProjector2D.inl255
-rw-r--r--include/astra/ParallelBeamStripKernelProjector2D.h1
-rw-r--r--include/astra/ParallelBeamStripKernelProjector2D.inl467
-rw-r--r--include/astra/ParallelProjectionGeometry2D.h16
-rw-r--r--include/astra/ParallelVecProjectionGeometry2D.h163
-rw-r--r--include/astra/ProjectionGeometry2D.h19
-rw-r--r--matlab/algorithms/DART/IterativeTomography.m2
-rw-r--r--matlab/algorithms/DART/examples/example1.m53
-rw-r--r--matlab/mex/astra_mex_data2d_c.cpp5
-rw-r--r--matlab/tools/astra_create_proj_geom.m13
-rw-r--r--matlab/tools/astra_geom_2vec.m76
-rw-r--r--matlab/tools/astra_geom_postalignment.m33
-rw-r--r--matlab/tools/astra_geom_size.m36
-rw-r--r--matlab/tools/astra_geom_visualize.m216
-rw-r--r--python/astra/PyIncludes.pxd4
-rw-r--r--python/astra/creators.py13
-rw-r--r--python/astra/data2d_c.pyx4
-rw-r--r--python/astra/functions.py56
-rw-r--r--python/astra/pythonutils.py2
-rw-r--r--samples/matlab/s014_FBP.m2
-rw-r--r--samples/matlab/s022_fbp_cor.m69
-rw-r--r--samples/python/s022_fbp_cor.py98
-rw-r--r--src/ConeProjectionGeometry3D.cpp2
-rw-r--r--src/CudaFDKAlgorithm3D.cpp2
-rw-r--r--src/CudaFilteredBackProjectionAlgorithm.cpp219
-rw-r--r--src/CudaForwardProjectionAlgorithm.cpp66
-rw-r--r--src/CudaReconstructionAlgorithm2D.cpp63
-rw-r--r--src/FanFlatBeamLineKernelProjector2D.cpp2
-rw-r--r--src/FanFlatBeamStripKernelProjector2D.cpp2
-rw-r--r--src/FanFlatProjectionGeometry2D.cpp18
-rw-r--r--src/Float32Data2D.cpp2
-rw-r--r--src/GeometryUtil2D.cpp175
-rw-r--r--src/ParallelBeamBlobKernelProjector2D.cpp93
-rw-r--r--src/ParallelBeamLineKernelProjector2D.cpp110
-rw-r--r--src/ParallelBeamLinearKernelProjector2D.cpp4
-rw-r--r--src/ParallelBeamStripKernelProjector2D.cpp102
-rw-r--r--src/ParallelProjectionGeometry2D.cpp38
-rw-r--r--src/ParallelVecProjectionGeometry2D.cpp233
-rw-r--r--src/ProjectionGeometry2D.cpp26
-rw-r--r--src/Projector2D.cpp5
-rw-r--r--src/VolumeGeometry2D.cpp20
-rw-r--r--tests/python/test_line2d.py310
-rw-r--r--tests/test_ParallelBeamLineKernelProjector2D.cpp2
-rw-r--r--tests/test_ParallelBeamLinearKernelProjector2D.cpp4
77 files changed, 3547 insertions, 2823 deletions
diff --git a/astra_vc14.vcxproj b/astra_vc14.vcxproj
index 36d37ec..cd91065 100644
--- a/astra_vc14.vcxproj
+++ b/astra_vc14.vcxproj
@@ -524,6 +524,7 @@
<ClCompile Include="src\Float32VolumeData3DMemory.cpp" />
<ClCompile Include="src\ForwardProjectionAlgorithm.cpp" />
<ClCompile Include="src\Fourier.cpp" />
+ <ClCompile Include="src\GeometryUtil2D.cpp" />
<ClCompile Include="src\GeometryUtil3D.cpp" />
<ClCompile Include="src\Globals.cpp" />
<ClCompile Include="src\Logging.cpp" />
@@ -533,6 +534,7 @@
<ClCompile Include="src\ParallelBeamStripKernelProjector2D.cpp" />
<ClCompile Include="src\ParallelProjectionGeometry2D.cpp" />
<ClCompile Include="src\ParallelProjectionGeometry3D.cpp" />
+ <ClCompile Include="src\ParallelVecProjectionGeometry2D.cpp" />
<ClCompile Include="src\ParallelVecProjectionGeometry3D.cpp" />
<ClCompile Include="src\PlatformDepSystemCode.cpp" />
<ClCompile Include="src\PluginAlgorithm.cpp" />
@@ -563,6 +565,7 @@
<ClInclude Include="cuda\2d\em.h" />
<ClInclude Include="cuda\2d\fan_bp.h" />
<ClInclude Include="cuda\2d\fan_fp.h" />
+ <ClInclude Include="cuda\2d\fbp.h" />
<ClInclude Include="cuda\2d\fbp_filters.h" />
<ClInclude Include="cuda\2d\fft.h" />
<ClInclude Include="cuda\2d\par_bp.h" />
@@ -649,6 +652,7 @@
<ClInclude Include="include\astra\ParallelBeamStripKernelProjector2D.h" />
<ClInclude Include="include\astra\ParallelProjectionGeometry2D.h" />
<ClInclude Include="include\astra\ParallelProjectionGeometry3D.h" />
+ <ClInclude Include="include\astra\ParallelVecProjectionGeometry2D.h" />
<ClInclude Include="include\astra\ParallelVecProjectionGeometry3D.h" />
<ClInclude Include="include\astra\PlatformDepSystemCode.h" />
<ClInclude Include="include\astra\PluginAlgorithm.h" />
@@ -723,6 +727,12 @@
<ExcludedFromBuild Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">true</ExcludedFromBuild>
<ExcludedFromBuild Condition="'$(Configuration)|$(Platform)'=='Release|x64'">true</ExcludedFromBuild>
</CudaCompile>
+ <CudaCompile Include="cuda\2d\fbp.cu">
+ <ExcludedFromBuild Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">true</ExcludedFromBuild>
+ <ExcludedFromBuild Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">true</ExcludedFromBuild>
+ <ExcludedFromBuild Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">true</ExcludedFromBuild>
+ <ExcludedFromBuild Condition="'$(Configuration)|$(Platform)'=='Release|x64'">true</ExcludedFromBuild>
+ </CudaCompile>
<CudaCompile Include="cuda\2d\fft.cu">
<ExcludedFromBuild Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">true</ExcludedFromBuild>
<ExcludedFromBuild Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">true</ExcludedFromBuild>
diff --git a/astra_vc14.vcxproj.filters b/astra_vc14.vcxproj.filters
index 591a4c7..24c54ec 100644
--- a/astra_vc14.vcxproj.filters
+++ b/astra_vc14.vcxproj.filters
@@ -25,6 +25,9 @@
<CudaCompile Include="cuda\2d\fan_fp.cu">
<Filter>CUDA\cuda source</Filter>
</CudaCompile>
+ <CudaCompile Include="cuda\2d\fbp.cu">
+ <Filter>CUDA\cuda source</Filter>
+ </CudaCompile>
<CudaCompile Include="cuda\2d\fft.cu">
<Filter>CUDA\cuda source</Filter>
</CudaCompile>
@@ -198,6 +201,9 @@
<ClCompile Include="src\FanFlatVecProjectionGeometry2D.cpp">
<Filter>Geometries\source</Filter>
</ClCompile>
+ <ClCompile Include="src\GeometryUtil2D.cpp">
+ <Filter>Geometries\source</Filter>
+ </ClCompile>
<ClCompile Include="src\GeometryUtil3D.cpp">
<Filter>Geometries\source</Filter>
</ClCompile>
@@ -207,6 +213,9 @@
<ClCompile Include="src\ParallelProjectionGeometry3D.cpp">
<Filter>Geometries\source</Filter>
</ClCompile>
+ <ClCompile Include="src\ParallelVecProjectionGeometry2D.cpp">
+ <Filter>Geometries\source</Filter>
+ </ClCompile>
<ClCompile Include="src\ParallelVecProjectionGeometry3D.cpp">
<Filter>Geometries\source</Filter>
</ClCompile>
@@ -479,6 +488,9 @@
<ClInclude Include="include\astra\ParallelProjectionGeometry3D.h">
<Filter>Geometries\headers</Filter>
</ClInclude>
+ <ClInclude Include="include\astra\ParallelVecProjectionGeometry2D.h">
+ <Filter>Geometries\headers</Filter>
+ </ClInclude>
<ClInclude Include="include\astra\ParallelVecProjectionGeometry3D.h">
<Filter>Geometries\headers</Filter>
</ClInclude>
@@ -629,6 +641,9 @@
<ClInclude Include="cuda\2d\fbp_filters.h">
<Filter>CUDA\cuda headers</Filter>
</ClInclude>
+ <ClInclude Include="cuda\2d\fbp.h">
+ <Filter>CUDA\cuda headers</Filter>
+ </ClInclude>
<ClInclude Include="cuda\2d\fft.h">
<Filter>CUDA\cuda headers</Filter>
</ClInclude>
diff --git a/build/linux/Makefile.in b/build/linux/Makefile.in
index 7cff15e..77e89e7 100644
--- a/build/linux/Makefile.in
+++ b/build/linux/Makefile.in
@@ -154,6 +154,7 @@ BASE_OBJECTS=\
src/Float32VolumeData3DMemory.lo \
src/ForwardProjectionAlgorithm.lo \
src/Fourier.lo \
+ src/GeometryUtil2D.lo \
src/GeometryUtil3D.lo \
src/Globals.lo \
src/Logging.lo \
@@ -162,6 +163,7 @@ BASE_OBJECTS=\
src/ParallelBeamLineKernelProjector2D.lo \
src/ParallelBeamStripKernelProjector2D.lo \
src/ParallelProjectionGeometry2D.lo \
+ src/ParallelVecProjectionGeometry2D.lo \
src/ParallelProjectionGeometry3D.lo \
src/ParallelVecProjectionGeometry3D.lo \
src/PlatformDepSystemCode.lo \
@@ -213,6 +215,7 @@ CUDA_OBJECTS=\
cuda/2d/par_bp.lo \
cuda/2d/fan_fp.lo \
cuda/2d/fan_bp.lo \
+ cuda/2d/fbp.lo \
cuda/2d/sirt.lo \
cuda/2d/sart.lo \
cuda/2d/cgls.lo \
diff --git a/build/msvc/gen.py b/build/msvc/gen.py
index 9c14ffe..771c226 100644
--- a/build/msvc/gen.py
+++ b/build/msvc/gen.py
@@ -158,6 +158,7 @@ P_astra["filters"]["CUDA\\cuda source"] = [
"cuda\\2d\\em.cu",
"cuda\\2d\\fan_bp.cu",
"cuda\\2d\\fan_fp.cu",
+"cuda\\2d\\fbp.cu",
"cuda\\2d\\fft.cu",
"cuda\\2d\\par_bp.cu",
"cuda\\2d\\par_fp.cu",
@@ -227,9 +228,11 @@ P_astra["filters"]["Geometries\\source"] = [
"src\\ConeVecProjectionGeometry3D.cpp",
"src\\FanFlatProjectionGeometry2D.cpp",
"src\\FanFlatVecProjectionGeometry2D.cpp",
+"src\\GeometryUtil2D.cpp",
"src\\GeometryUtil3D.cpp",
"src\\ParallelProjectionGeometry2D.cpp",
"src\\ParallelProjectionGeometry3D.cpp",
+"src\\ParallelVecProjectionGeometry2D.cpp",
"src\\ParallelVecProjectionGeometry3D.cpp",
"src\\ProjectionGeometry2D.cpp",
"src\\ProjectionGeometry3D.cpp",
@@ -290,6 +293,7 @@ P_astra["filters"]["CUDA\\cuda headers"] = [
"cuda\\2d\\fan_bp.h",
"cuda\\2d\\fan_fp.h",
"cuda\\2d\\fbp_filters.h",
+"cuda\\2d\\fbp.h",
"cuda\\2d\\fft.h",
"cuda\\2d\\par_bp.h",
"cuda\\2d\\par_fp.h",
@@ -371,6 +375,7 @@ P_astra["filters"]["Geometries\\headers"] = [
"include\\astra\\GeometryUtil3D.h",
"include\\astra\\ParallelProjectionGeometry2D.h",
"include\\astra\\ParallelProjectionGeometry3D.h",
+"include\\astra\\ParallelVecProjectionGeometry2D.h",
"include\\astra\\ParallelVecProjectionGeometry3D.h",
"include\\astra\\ProjectionGeometry2D.h",
"include\\astra\\ProjectionGeometry3D.h",
diff --git a/cuda/2d/algo.cu b/cuda/2d/algo.cu
index 0e5209f..3275f6d 100644
--- a/cuda/2d/algo.cu
+++ b/cuda/2d/algo.cu
@@ -34,13 +34,13 @@ along with the ASTRA Toolbox. If not, see <http://www.gnu.org/licenses/>.
#include "fan_bp.h"
#include "util.h"
#include "arith.h"
+#include "astra.h"
namespace astraCUDA {
ReconAlgo::ReconAlgo()
{
- angles = 0;
- TOffsets = 0;
+ parProjs = 0;
fanProjs = 0;
shouldAbort = false;
@@ -65,8 +65,7 @@ ReconAlgo::~ReconAlgo()
void ReconAlgo::reset()
{
- delete[] angles;
- delete[] TOffsets;
+ delete[] parProjs;
delete[] fanProjs;
if (freeGPUMemory) {
@@ -76,8 +75,7 @@ void ReconAlgo::reset()
cudaFree(D_volumeData);
}
- angles = 0;
- TOffsets = 0;
+ parProjs = 0;
fanProjs = 0;
shouldAbort = false;
@@ -123,46 +121,40 @@ bool ReconAlgo::enableSinogramMask()
}
-bool ReconAlgo::setGeometry(const SDimensions& _dims, const float* _angles)
+bool ReconAlgo::setGeometry(const astra::CVolumeGeometry2D* pVolGeom,
+ const astra::CProjectionGeometry2D* pProjGeom)
{
- dims = _dims;
+ bool ok;
- angles = new float[dims.iProjAngles];
+ ok = convertAstraGeometry_dims(pVolGeom, pProjGeom, dims);
- memcpy(angles, _angles, sizeof(angles[0]) * dims.iProjAngles);
+ if (!ok)
+ return false;
+ delete[] parProjs;
+ parProjs = 0;
delete[] fanProjs;
fanProjs = 0;
- return true;
-}
-
-bool ReconAlgo::setFanGeometry(const SDimensions& _dims,
- const SFanProjection* _projs)
-{
- dims = _dims;
- fanProjs = new SFanProjection[dims.iProjAngles];
-
- memcpy(fanProjs, _projs, sizeof(fanProjs[0]) * dims.iProjAngles);
-
- delete[] angles;
- angles = 0;
+ fOutputScale = 1.0f;
+ ok = convertAstraGeometry(pVolGeom, pProjGeom, parProjs, fanProjs, fOutputScale);
+ if (!ok)
+ return false;
return true;
}
-
-bool ReconAlgo::setTOffsets(const float* _TOffsets)
+bool ReconAlgo::setSuperSampling(int raysPerDet, int raysPerPixelDim)
{
- // TODO: determine if they're all zero?
- TOffsets = new float[dims.iProjAngles];
- memcpy(TOffsets, _TOffsets, sizeof(angles[0]) * dims.iProjAngles);
+ if (raysPerDet <= 0 || raysPerPixelDim <= 0)
+ return false;
+
+ dims.iRaysPerDet = raysPerDet;
+ dims.iRaysPerPixelDim = raysPerPixelDim;
return true;
}
-
-
bool ReconAlgo::setVolumeMask(float* _D_maskData, unsigned int _maskPitch)
{
assert(useVolumeMask);
@@ -323,14 +315,14 @@ bool ReconAlgo::callFP(float* D_volumeData, unsigned int volumePitch,
float* D_projData, unsigned int projPitch,
float outputScale)
{
- if (angles) {
+ if (parProjs) {
assert(!fanProjs);
return FP(D_volumeData, volumePitch, D_projData, projPitch,
- dims, angles, TOffsets, outputScale);
+ dims, parProjs, fOutputScale * outputScale);
} else {
assert(fanProjs);
return FanFP(D_volumeData, volumePitch, D_projData, projPitch,
- dims, fanProjs, outputScale);
+ dims, fanProjs, fOutputScale * outputScale);
}
}
@@ -338,10 +330,10 @@ bool ReconAlgo::callBP(float* D_volumeData, unsigned int volumePitch,
float* D_projData, unsigned int projPitch,
float outputScale)
{
- if (angles) {
+ if (parProjs) {
assert(!fanProjs);
return BP(D_volumeData, volumePitch, D_projData, projPitch,
- dims, angles, TOffsets, outputScale);
+ dims, parProjs, outputScale);
} else {
assert(fanProjs);
return FanBP(D_volumeData, volumePitch, D_projData, projPitch,
diff --git a/cuda/2d/algo.h b/cuda/2d/algo.h
index 4a75907..d70859f 100644
--- a/cuda/2d/algo.h
+++ b/cuda/2d/algo.h
@@ -31,6 +31,17 @@ along with the ASTRA Toolbox. If not, see <http://www.gnu.org/licenses/>.
#include "astra/Globals.h"
#include "dims.h"
+namespace astra {
+
+class CParallelProjectionGeometry2D;
+class CParallelVecProjectionGeometry2D;
+class CFanFlatProjectionGeometry2D;
+class CFanFlatVecProjectionGeometry2D;
+class CVolumeGeometry2D;
+class CProjectionGeometry2D;
+
+}
+
namespace astraCUDA {
class _AstraExport ReconAlgo {
@@ -40,11 +51,10 @@ public:
bool setGPUIndex(int iGPUIndex);
- bool setGeometry(const SDimensions& dims, const float* angles);
- bool setFanGeometry(const SDimensions& dims, const SFanProjection* projs);
+ bool setGeometry(const astra::CVolumeGeometry2D* pVolGeom,
+ const astra::CProjectionGeometry2D* pProjGeom);
- // setTOffsets should (optionally) be called after setGeometry
- bool setTOffsets(const float* TOffsets);
+ bool setSuperSampling(int raysPerDet, int raysPerPixelDim);
void signalAbort() { shouldAbort = true; }
@@ -123,9 +133,9 @@ protected:
SDimensions dims;
- float* angles;
- float* TOffsets;
+ SParProjection* parProjs;
SFanProjection* fanProjs;
+ float fOutputScale;
volatile bool shouldAbort;
diff --git a/cuda/2d/astra.cu b/cuda/2d/astra.cu
index c0132b2..81459de 100644
--- a/cuda/2d/astra.cu
+++ b/cuda/2d/astra.cu
@@ -41,8 +41,10 @@ along with the ASTRA Toolbox. If not, see <http://www.gnu.org/licenses/>.
#include <fstream>
#include <cuda.h>
+#include "../../include/astra/GeometryUtil2D.h"
#include "../../include/astra/VolumeGeometry2D.h"
#include "../../include/astra/ParallelProjectionGeometry2D.h"
+#include "../../include/astra/ParallelVecProjectionGeometry2D.h"
#include "../../include/astra/FanFlatProjectionGeometry2D.h"
#include "../../include/astra/FanFlatVecProjectionGeometry2D.h"
@@ -63,514 +65,6 @@ enum CUDAProjectionType {
};
-class AstraFBP_internal {
-public:
- SDimensions dims;
- float* angles;
- float* TOffsets;
- astraCUDA::SFanProjection* fanProjections;
-
- float fOriginSourceDistance;
- float fOriginDetectorDistance;
-
- float fPixelSize;
-
- bool bFanBeam;
- bool bShortScan;
-
- bool initialized;
- bool setStartReconstruction;
-
- float* D_sinoData;
- unsigned int sinoPitch;
-
- float* D_volumeData;
- unsigned int volumePitch;
-
- cufftComplex * m_pDevFilter;
-};
-
-AstraFBP::AstraFBP()
-{
- pData = new AstraFBP_internal();
-
- pData->angles = 0;
- pData->fanProjections = 0;
- pData->TOffsets = 0;
- pData->D_sinoData = 0;
- pData->D_volumeData = 0;
-
- pData->dims.iVolWidth = 0;
- pData->dims.iProjAngles = 0;
- pData->dims.fDetScale = 1.0f;
- pData->dims.iRaysPerDet = 1;
- pData->dims.iRaysPerPixelDim = 1;
-
- pData->initialized = false;
- pData->setStartReconstruction = false;
-
- pData->m_pDevFilter = NULL;
-}
-
-AstraFBP::~AstraFBP()
-{
- delete[] pData->angles;
- pData->angles = 0;
-
- delete[] pData->TOffsets;
- pData->TOffsets = 0;
-
- delete[] pData->fanProjections;
- pData->fanProjections = 0;
-
- cudaFree(pData->D_sinoData);
- pData->D_sinoData = 0;
-
- cudaFree(pData->D_volumeData);
- pData->D_volumeData = 0;
-
- if(pData->m_pDevFilter != NULL)
- {
- freeComplexOnDevice(pData->m_pDevFilter);
- pData->m_pDevFilter = NULL;
- }
-
- delete pData;
- pData = 0;
-}
-
-bool AstraFBP::setReconstructionGeometry(unsigned int iVolWidth,
- unsigned int iVolHeight,
- float fPixelSize)
-{
- if (pData->initialized)
- return false;
-
- pData->dims.iVolWidth = iVolWidth;
- pData->dims.iVolHeight = iVolHeight;
-
- pData->fPixelSize = fPixelSize;
-
- return (iVolWidth > 0 && iVolHeight > 0 && fPixelSize > 0.0f);
-}
-
-bool AstraFBP::setProjectionGeometry(unsigned int iProjAngles,
- unsigned int iProjDets,
- const float* pfAngles,
- float fDetSize)
-{
- if (pData->initialized)
- return false;
-
- pData->dims.iProjAngles = iProjAngles;
- pData->dims.iProjDets = iProjDets;
- pData->dims.fDetScale = fDetSize / pData->fPixelSize;
-
- if (iProjAngles == 0 || iProjDets == 0 || pfAngles == 0)
- return false;
-
- pData->angles = new float[iProjAngles];
- memcpy(pData->angles, pfAngles, iProjAngles * sizeof(pfAngles[0]));
-
- pData->bFanBeam = false;
-
- return true;
-}
-
-bool AstraFBP::setFanGeometry(unsigned int iProjAngles,
- unsigned int iProjDets,
- const astraCUDA::SFanProjection *fanProjs,
- const float* pfAngles,
- float fOriginSourceDistance,
- float fOriginDetectorDistance,
- float fDetSize,
- bool bShortScan)
-{
- // Slightly abusing setProjectionGeometry for this...
- if (!setProjectionGeometry(iProjAngles, iProjDets, pfAngles, fDetSize))
- return false;
-
- pData->fOriginSourceDistance = fOriginSourceDistance;
- pData->fOriginDetectorDistance = fOriginDetectorDistance;
-
- pData->fanProjections = new astraCUDA::SFanProjection[iProjAngles];
- memcpy(pData->fanProjections, fanProjs, iProjAngles * sizeof(fanProjs[0]));
-
- pData->bFanBeam = true;
- pData->bShortScan = bShortScan;
-
- return true;
-}
-
-
-bool AstraFBP::setPixelSuperSampling(unsigned int iPixelSuperSampling)
-{
- if (pData->initialized)
- return false;
-
- if (iPixelSuperSampling == 0)
- return false;
-
- pData->dims.iRaysPerPixelDim = iPixelSuperSampling;
-
- return true;
-}
-
-
-bool AstraFBP::setTOffsets(const float* pfTOffsets)
-{
- if (pData->initialized)
- return false;
-
- if (pfTOffsets == 0)
- return false;
-
- pData->TOffsets = new float[pData->dims.iProjAngles];
- memcpy(pData->TOffsets, pfTOffsets, pData->dims.iProjAngles * sizeof(pfTOffsets[0]));
-
- return true;
-}
-
-bool AstraFBP::init(int iGPUIndex)
-{
- if (pData->initialized)
- {
- return false;
- }
-
- if (pData->dims.iProjAngles == 0 || pData->dims.iVolWidth == 0)
- {
- return false;
- }
-
- if (iGPUIndex != -1) {
- cudaSetDevice(iGPUIndex);
- cudaError_t err = cudaGetLastError();
-
- // Ignore errors caused by calling cudaSetDevice multiple times
- if (err != cudaSuccess && err != cudaErrorSetOnActiveProcess)
- {
- return false;
- }
- }
-
- bool ok = allocateVolumeData(pData->D_volumeData, pData->volumePitch, pData->dims);
- if (!ok)
- {
- return false;
- }
-
- ok = allocateProjectionData(pData->D_sinoData, pData->sinoPitch, pData->dims);
- if (!ok)
- {
- cudaFree(pData->D_volumeData);
- pData->D_volumeData = 0;
- return false;
- }
-
- pData->initialized = true;
-
- return true;
-}
-
-bool AstraFBP::setSinogram(const float* pfSinogram,
- unsigned int iSinogramPitch)
-{
- if (!pData->initialized)
- return false;
- if (!pfSinogram)
- return false;
-
- bool ok = copySinogramToDevice(pfSinogram, iSinogramPitch,
- pData->dims,
- pData->D_sinoData, pData->sinoPitch);
- if (!ok)
- return false;
-
- // rescale sinogram to adjust for pixel size
- processSino<opMul>(pData->D_sinoData,
- 1.0f/(pData->fPixelSize*pData->fPixelSize),
- pData->sinoPitch, pData->dims);
-
- pData->setStartReconstruction = false;
-
- return true;
-}
-
-static int calcNextPowerOfTwo(int _iValue)
-{
- int iOutput = 1;
-
- while(iOutput < _iValue)
- {
- iOutput *= 2;
- }
-
- return iOutput;
-}
-
-bool AstraFBP::run()
-{
- if (!pData->initialized)
- {
- return false;
- }
-
- zeroVolumeData(pData->D_volumeData, pData->volumePitch, pData->dims);
-
- bool ok = false;
-
- if (pData->bFanBeam) {
- // Call FDK_PreWeight to handle fan beam geometry. We treat
- // this as a cone beam setup of a single slice:
-
- // TODO: TOffsets affects this preweighting...
-
- // We create a fake cudaPitchedPtr
- cudaPitchedPtr tmp;
- tmp.ptr = pData->D_sinoData;
- tmp.pitch = pData->sinoPitch * sizeof(float);
- tmp.xsize = pData->dims.iProjDets;
- tmp.ysize = pData->dims.iProjAngles;
- // and a fake Dimensions3D
- astraCUDA3d::SDimensions3D dims3d;
- dims3d.iVolX = pData->dims.iVolWidth;
- dims3d.iVolY = pData->dims.iVolHeight;
- dims3d.iVolZ = 1;
- dims3d.iProjAngles = pData->dims.iProjAngles;
- dims3d.iProjU = pData->dims.iProjDets;
- dims3d.iProjV = 1;
-
- astraCUDA3d::FDK_PreWeight(tmp, pData->fOriginSourceDistance,
- pData->fOriginDetectorDistance, 0.0f,
- pData->dims.fDetScale, 1.0f, // TODO: Are these correct?
- pData->bShortScan, dims3d, pData->angles);
- }
-
- if (pData->m_pDevFilter) {
-
- int iFFTRealDetCount = calcNextPowerOfTwo(2 * pData->dims.iProjDets);
- int iFFTFourDetCount = calcFFTFourSize(iFFTRealDetCount);
-
- cufftComplex * pDevComplexSinogram = NULL;
-
- allocateComplexOnDevice(pData->dims.iProjAngles, iFFTFourDetCount, &pDevComplexSinogram);
-
- runCudaFFT(pData->dims.iProjAngles, pData->D_sinoData, pData->sinoPitch, pData->dims.iProjDets, iFFTRealDetCount, iFFTFourDetCount, pDevComplexSinogram);
-
- applyFilter(pData->dims.iProjAngles, iFFTFourDetCount, pDevComplexSinogram, pData->m_pDevFilter);
-
- runCudaIFFT(pData->dims.iProjAngles, pDevComplexSinogram, pData->D_sinoData, pData->sinoPitch, pData->dims.iProjDets, iFFTRealDetCount, iFFTFourDetCount);
-
- freeComplexOnDevice(pDevComplexSinogram);
-
- }
-
- float fOutputScale = (M_PI / 2.0f) / (float)pData->dims.iProjAngles;
-
- if (pData->bFanBeam) {
- ok = FanBP_FBPWeighted(pData->D_volumeData, pData->volumePitch, pData->D_sinoData, pData->sinoPitch, pData->dims, pData->fanProjections, fOutputScale);
-
- } else {
- ok = BP(pData->D_volumeData, pData->volumePitch, pData->D_sinoData, pData->sinoPitch, pData->dims, pData->angles, pData->TOffsets, fOutputScale);
- }
- if(!ok)
- {
- return false;
- }
-
- return true;
-}
-
-bool AstraFBP::getReconstruction(float* pfReconstruction, unsigned int iReconstructionPitch) const
-{
- if (!pData->initialized)
- return false;
-
- bool ok = copyVolumeFromDevice(pfReconstruction, iReconstructionPitch,
- pData->dims,
- pData->D_volumeData, pData->volumePitch);
- if (!ok)
- return false;
-
- return true;
-}
-
-int AstraFBP::calcFourierFilterSize(int _iDetectorCount)
-{
- int iFFTRealDetCount = calcNextPowerOfTwo(2 * _iDetectorCount);
- int iFreqBinCount = calcFFTFourSize(iFFTRealDetCount);
-
- // CHECKME: Matlab makes this at least 64. Do we also need to?
- return iFreqBinCount;
-}
-
-bool AstraFBP::setFilter(E_FBPFILTER _eFilter, const float * _pfHostFilter /* = NULL */, int _iFilterWidth /* = 0 */, float _fD /* = 1.0f */, float _fFilterParameter /* = -1.0f */)
-{
- if(pData->m_pDevFilter != 0)
- {
- freeComplexOnDevice(pData->m_pDevFilter);
- pData->m_pDevFilter = 0;
- }
-
- if (_eFilter == FILTER_NONE)
- return true; // leave pData->m_pDevFilter set to 0
-
-
- int iFFTRealDetCount = calcNextPowerOfTwo(2 * pData->dims.iProjDets);
- int iFreqBinCount = calcFFTFourSize(iFFTRealDetCount);
-
- cufftComplex * pHostFilter = new cufftComplex[pData->dims.iProjAngles * iFreqBinCount];
- memset(pHostFilter, 0, sizeof(cufftComplex) * pData->dims.iProjAngles * iFreqBinCount);
-
- allocateComplexOnDevice(pData->dims.iProjAngles, iFreqBinCount, &(pData->m_pDevFilter));
-
- switch(_eFilter)
- {
- case FILTER_NONE:
- // handled above
- break;
- case FILTER_RAMLAK:
- case FILTER_SHEPPLOGAN:
- case FILTER_COSINE:
- case FILTER_HAMMING:
- case FILTER_HANN:
- case FILTER_TUKEY:
- case FILTER_LANCZOS:
- case FILTER_TRIANGULAR:
- case FILTER_GAUSSIAN:
- case FILTER_BARTLETTHANN:
- case FILTER_BLACKMAN:
- case FILTER_NUTTALL:
- case FILTER_BLACKMANHARRIS:
- case FILTER_BLACKMANNUTTALL:
- case FILTER_FLATTOP:
- case FILTER_PARZEN:
- {
- genFilter(_eFilter, _fD, pData->dims.iProjAngles, pHostFilter, iFFTRealDetCount, iFreqBinCount, _fFilterParameter);
- uploadComplexArrayToDevice(pData->dims.iProjAngles, iFreqBinCount, pHostFilter, pData->m_pDevFilter);
-
- break;
- }
- case FILTER_PROJECTION:
- {
- // make sure the offered filter has the correct size
- assert(_iFilterWidth == iFreqBinCount);
-
- for(int iFreqBinIndex = 0; iFreqBinIndex < iFreqBinCount; iFreqBinIndex++)
- {
- float fValue = _pfHostFilter[iFreqBinIndex];
-
- for(int iProjectionIndex = 0; iProjectionIndex < (int)pData->dims.iProjAngles; iProjectionIndex++)
- {
- pHostFilter[iFreqBinIndex + iProjectionIndex * iFreqBinCount].x = fValue;
- pHostFilter[iFreqBinIndex + iProjectionIndex * iFreqBinCount].y = 0.0f;
- }
- }
- uploadComplexArrayToDevice(pData->dims.iProjAngles, iFreqBinCount, pHostFilter, pData->m_pDevFilter);
- break;
- }
- case FILTER_SINOGRAM:
- {
- // make sure the offered filter has the correct size
- assert(_iFilterWidth == iFreqBinCount);
-
- for(int iFreqBinIndex = 0; iFreqBinIndex < iFreqBinCount; iFreqBinIndex++)
- {
- for(int iProjectionIndex = 0; iProjectionIndex < (int)pData->dims.iProjAngles; iProjectionIndex++)
- {
- float fValue = _pfHostFilter[iFreqBinIndex + iProjectionIndex * _iFilterWidth];
-
- pHostFilter[iFreqBinIndex + iProjectionIndex * iFreqBinCount].x = fValue;
- pHostFilter[iFreqBinIndex + iProjectionIndex * iFreqBinCount].y = 0.0f;
- }
- }
- uploadComplexArrayToDevice(pData->dims.iProjAngles, iFreqBinCount, pHostFilter, pData->m_pDevFilter);
- break;
- }
- case FILTER_RPROJECTION:
- {
- int iProjectionCount = pData->dims.iProjAngles;
- int iRealFilterElementCount = iProjectionCount * iFFTRealDetCount;
- float * pfHostRealFilter = new float[iRealFilterElementCount];
- memset(pfHostRealFilter, 0, sizeof(float) * iRealFilterElementCount);
-
- int iUsedFilterWidth = min(_iFilterWidth, iFFTRealDetCount);
- int iStartFilterIndex = (_iFilterWidth - iUsedFilterWidth) / 2;
- int iMaxFilterIndex = iStartFilterIndex + iUsedFilterWidth;
-
- int iFilterShiftSize = _iFilterWidth / 2;
-
- for(int iDetectorIndex = iStartFilterIndex; iDetectorIndex < iMaxFilterIndex; iDetectorIndex++)
- {
- int iFFTInFilterIndex = (iDetectorIndex + iFFTRealDetCount - iFilterShiftSize) % iFFTRealDetCount;
- float fValue = _pfHostFilter[iDetectorIndex];
-
- for(int iProjectionIndex = 0; iProjectionIndex < (int)pData->dims.iProjAngles; iProjectionIndex++)
- {
- pfHostRealFilter[iFFTInFilterIndex + iProjectionIndex * iFFTRealDetCount] = fValue;
- }
- }
-
- float* pfDevRealFilter = NULL;
- cudaMalloc((void **)&pfDevRealFilter, sizeof(float) * iRealFilterElementCount); // TODO: check for errors
- cudaMemcpy(pfDevRealFilter, pfHostRealFilter, sizeof(float) * iRealFilterElementCount, cudaMemcpyHostToDevice);
- delete[] pfHostRealFilter;
-
- runCudaFFT(iProjectionCount, pfDevRealFilter, iFFTRealDetCount, iFFTRealDetCount, iFFTRealDetCount, iFreqBinCount, pData->m_pDevFilter);
-
- cudaFree(pfDevRealFilter);
-
- break;
- }
- case FILTER_RSINOGRAM:
- {
- int iProjectionCount = pData->dims.iProjAngles;
- int iRealFilterElementCount = iProjectionCount * iFFTRealDetCount;
- float* pfHostRealFilter = new float[iRealFilterElementCount];
- memset(pfHostRealFilter, 0, sizeof(float) * iRealFilterElementCount);
-
- int iUsedFilterWidth = min(_iFilterWidth, iFFTRealDetCount);
- int iStartFilterIndex = (_iFilterWidth - iUsedFilterWidth) / 2;
- int iMaxFilterIndex = iStartFilterIndex + iUsedFilterWidth;
-
- int iFilterShiftSize = _iFilterWidth / 2;
-
- for(int iDetectorIndex = iStartFilterIndex; iDetectorIndex < iMaxFilterIndex; iDetectorIndex++)
- {
- int iFFTInFilterIndex = (iDetectorIndex + iFFTRealDetCount - iFilterShiftSize) % iFFTRealDetCount;
-
- for(int iProjectionIndex = 0; iProjectionIndex < (int)pData->dims.iProjAngles; iProjectionIndex++)
- {
- float fValue = _pfHostFilter[iDetectorIndex + iProjectionIndex * _iFilterWidth];
- pfHostRealFilter[iFFTInFilterIndex + iProjectionIndex * iFFTRealDetCount] = fValue;
- }
- }
-
- float* pfDevRealFilter = NULL;
- cudaMalloc((void **)&pfDevRealFilter, sizeof(float) * iRealFilterElementCount); // TODO: check for errors
- cudaMemcpy(pfDevRealFilter, pfHostRealFilter, sizeof(float) * iRealFilterElementCount, cudaMemcpyHostToDevice);
- delete[] pfHostRealFilter;
-
- runCudaFFT(iProjectionCount, pfDevRealFilter, iFFTRealDetCount, iFFTRealDetCount, iFFTRealDetCount, iFreqBinCount, pData->m_pDevFilter);
-
- cudaFree(pfDevRealFilter);
-
- break;
- }
- default:
- {
- ASTRA_ERROR("AstraFBP::setFilter: Unknown filter type requested");
- delete [] pHostFilter;
- return false;
- }
- }
-
- delete [] pHostFilter;
-
- return true;
-}
-
BPalgo::BPalgo()
{
@@ -615,18 +109,17 @@ float BPalgo::computeDiffNorm()
bool astraCudaFP(const float* pfVolume, float* pfSinogram,
unsigned int iVolWidth, unsigned int iVolHeight,
unsigned int iProjAngles, unsigned int iProjDets,
- const float *pfAngles, const float *pfOffsets,
- float fDetSize, unsigned int iDetSuperSampling,
+ const SParProjection *pAngles,
+ unsigned int iDetSuperSampling,
float fOutputScale, int iGPUIndex)
{
SDimensions dims;
- if (iProjAngles == 0 || iProjDets == 0 || pfAngles == 0)
+ if (iProjAngles == 0 || iProjDets == 0 || pAngles == 0)
return false;
dims.iProjAngles = iProjAngles;
dims.iProjDets = iProjDets;
- dims.fDetScale = fDetSize;
if (iDetSuperSampling == 0)
return false;
@@ -676,7 +169,7 @@ bool astraCudaFP(const float* pfVolume, float* pfSinogram,
}
zeroProjectionData(D_sinoData, sinoPitch, dims);
- ok = FP(D_volumeData, volumePitch, D_sinoData, sinoPitch, dims, pfAngles, pfOffsets, fOutputScale);
+ ok = FP(D_volumeData, volumePitch, D_sinoData, sinoPitch, dims, pAngles, fOutputScale);
if (!ok) {
cudaFree(D_volumeData);
cudaFree(D_sinoData);
@@ -711,7 +204,6 @@ bool astraCudaFanFP(const float* pfVolume, float* pfSinogram,
dims.iProjAngles = iProjAngles;
dims.iProjDets = iProjDets;
- dims.fDetScale = 1.0f; // TODO?
if (iDetSuperSampling == 0)
return false;
@@ -787,123 +279,99 @@ bool astraCudaFanFP(const float* pfVolume, float* pfSinogram,
}
-bool convertAstraGeometry(const CVolumeGeometry2D* pVolGeom,
- const CParallelProjectionGeometry2D* pProjGeom,
- float*& detectorOffsets, float*& projectionAngles,
- float& detSize, float& outputScale)
+// adjust pProjs to normalize volume geometry
+template<typename ProjectionT>
+static bool convertAstraGeometry_internal(const CVolumeGeometry2D* pVolGeom,
+ unsigned int iProjectionAngleCount,
+ ProjectionT*& pProjs,
+ float& fOutputScale)
{
- assert(pVolGeom);
- assert(pProjGeom);
- assert(pProjGeom->getProjectionAngles());
-
+ // TODO: Make EPS relative
const float EPS = 0.00001f;
- int nth = pProjGeom->getProjectionAngleCount();
-
// Check if pixels are square
if (abs(pVolGeom->getPixelLengthX() - pVolGeom->getPixelLengthY()) > EPS)
return false;
+ float dx = -(pVolGeom->getWindowMinX() + pVolGeom->getWindowMaxX()) / 2;
+ float dy = -(pVolGeom->getWindowMinY() + pVolGeom->getWindowMaxY()) / 2;
- // Scale volume pixels to 1x1
- detSize = pProjGeom->getDetectorWidth() / pVolGeom->getPixelLengthX();
+ float factor = 1.0f / pVolGeom->getPixelLengthX();
- // Copy angles
- float *angles = new float[nth];
- for (int i = 0; i < nth; ++i)
- angles[i] = pProjGeom->getProjectionAngles()[i];
- projectionAngles = angles;
-
- // Check if we need to translate
- bool offCenter = false;
- if (abs(pVolGeom->getWindowMinX() + pVolGeom->getWindowMaxX()) > EPS ||
- abs(pVolGeom->getWindowMinY() + pVolGeom->getWindowMaxY()) > EPS)
- {
- offCenter = true;
+ for (int i = 0; i < iProjectionAngleCount; ++i) {
+ // CHECKME: Order of scaling and translation
+ pProjs[i].translate(dx, dy);
+ pProjs[i].scale(factor);
}
+ // CHECKME: Check factor
+ fOutputScale *= pVolGeom->getPixelLengthX() * pVolGeom->getPixelLengthY();
- // If there are existing detector offsets, or if we need to translate,
- // we need to return offsets
- if (pProjGeom->getExtraDetectorOffset() || 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;
- }
+ return true;
+}
- if (offCenter) {
- float dx = (pVolGeom->getWindowMinX() + pVolGeom->getWindowMaxX()) / 2;
- float dy = (pVolGeom->getWindowMinY() + pVolGeom->getWindowMaxY()) / 2;
- // CHECKME: Is d in pixels or in units?
- for (int i = 0; i < nth; ++i) {
- float d = dx * cos(angles[i]) + dy * sin(angles[i]);
- offset[i] += d;
- }
- }
+bool convertAstraGeometry(const CVolumeGeometry2D* pVolGeom,
+ const CParallelProjectionGeometry2D* pProjGeom,
+ SParProjection*& pProjs,
+ float& fOutputScale)
+{
+ assert(pVolGeom);
+ assert(pProjGeom);
+ assert(pProjGeom->getProjectionAngles());
- // CHECKME: Order of scaling and translation
+ int nth = pProjGeom->getProjectionAngleCount();
- // Scale volume pixels to 1x1
- for (int i = 0; i < nth; ++i) {
- //offset[i] /= pVolGeom->getPixelLengthX();
- //offset[i] *= detSize;
- }
+ pProjs = genParProjections(nth,
+ pProjGeom->getDetectorCount(),
+ pProjGeom->getDetectorWidth(),
+ pProjGeom->getProjectionAngles(), 0);
+ bool ok;
+ fOutputScale = 1.0f;
- detectorOffsets = offset;
- } else {
- detectorOffsets = 0;
- }
+ ok = convertAstraGeometry_internal(pVolGeom, nth, pProjs, fOutputScale);
- outputScale = pVolGeom->getPixelLengthX();
- outputScale *= outputScale;
+ if (!ok) {
+ delete[] pProjs;
+ pProjs = 0;
+ }
- return true;
+ return ok;
}
-static void convertAstraGeometry_internal(const CVolumeGeometry2D* pVolGeom,
- unsigned int iProjectionAngleCount,
- astraCUDA::SFanProjection*& pProjs,
- float& outputScale)
+bool convertAstraGeometry(const CVolumeGeometry2D* pVolGeom,
+ const CParallelVecProjectionGeometry2D* pProjGeom,
+ SParProjection*& pProjs,
+ float& fOutputScale)
{
- // Translate
- float dx = (pVolGeom->getWindowMinX() + pVolGeom->getWindowMaxX()) / 2;
- float dy = (pVolGeom->getWindowMinY() + pVolGeom->getWindowMaxY()) / 2;
+ assert(pVolGeom);
+ assert(pProjGeom);
+ assert(pProjGeom->getProjectionVectors());
- for (int i = 0; i < iProjectionAngleCount; ++i) {
- pProjs[i].fSrcX -= dx;
- pProjs[i].fSrcY -= dy;
- pProjs[i].fDetSX -= dx;
- pProjs[i].fDetSY -= dy;
+ int nth = pProjGeom->getProjectionAngleCount();
+
+ pProjs = new SParProjection[nth];
+
+ for (int i = 0; i < nth; ++i) {
+ pProjs[i] = pProjGeom->getProjectionVectors()[i];
}
- // CHECKME: Order of scaling and translation
+ bool ok;
+ fOutputScale = 1.0f;
- // Scale
- float factor = 1.0f / pVolGeom->getPixelLengthX();
- for (int i = 0; i < iProjectionAngleCount; ++i) {
- pProjs[i].fSrcX *= factor;
- pProjs[i].fSrcY *= factor;
- pProjs[i].fDetSX *= factor;
- pProjs[i].fDetSY *= factor;
- pProjs[i].fDetUX *= factor;
- pProjs[i].fDetUY *= factor;
+ ok = convertAstraGeometry_internal(pVolGeom, nth, pProjs, fOutputScale);
+ if (!ok) {
+ delete[] pProjs;
+ pProjs = 0;
}
- // CHECKME: Check factor
- outputScale = pVolGeom->getPixelLengthX();
-// outputScale *= outputScale;
+ return ok;
}
+
bool convertAstraGeometry(const CVolumeGeometry2D* pVolGeom,
const CFanFlatProjectionGeometry2D* pProjGeom,
astraCUDA::SFanProjection*& pProjs,
@@ -913,6 +381,7 @@ bool convertAstraGeometry(const CVolumeGeometry2D* pVolGeom,
assert(pProjGeom);
assert(pProjGeom->getProjectionAngles());
+ // TODO: Make EPS relative
const float EPS = 0.00001f;
int nth = pProjGeom->getProjectionAngleCount();
@@ -931,23 +400,9 @@ bool convertAstraGeometry(const CVolumeGeometry2D* pVolGeom,
float fDetSize = pProjGeom->getDetectorWidth();
const float *pfAngles = pProjGeom->getProjectionAngles();
- pProjs = new SFanProjection[nth];
-
- float fSrcX0 = 0.0f;
- float fSrcY0 = -fOriginSourceDistance;
- float fDetUX0 = fDetSize;
- float fDetUY0 = 0.0f;
- float fDetSX0 = pProjGeom->getDetectorCount() * fDetUX0 / -2.0f;
- float fDetSY0 = fOriginDetectorDistance;
-
-#define ROTATE0(name,i,alpha) do { pProjs[i].f##name##X = f##name##X0 * cos(alpha) - f##name##Y0 * sin(alpha); pProjs[i].f##name##Y = f##name##X0 * sin(alpha) + f##name##Y0 * cos(alpha); } while(0)
- for (int i = 0; i < nth; ++i) {
- ROTATE0(Src, i, pfAngles[i]);
- ROTATE0(DetS, i, pfAngles[i]);
- ROTATE0(DetU, i, pfAngles[i]);
- }
-
-#undef ROTATE0
+ pProjs = genFanProjections(nth, pProjGeom->getDetectorCount(),
+ fOriginSourceDistance, fOriginDetectorDistance,
+ fDetSize, pfAngles);
convertAstraGeometry_internal(pVolGeom, nth, pProjs, outputScale);
@@ -964,6 +419,7 @@ bool convertAstraGeometry(const CVolumeGeometry2D* pVolGeom,
assert(pProjGeom);
assert(pProjGeom->getProjectionVectors());
+ // TODO: Make EPS relative
const float EPS = 0.00001f;
int nx = pVolGeom->getGridColCount();
@@ -985,6 +441,51 @@ bool convertAstraGeometry(const CVolumeGeometry2D* pVolGeom,
return true;
}
+bool convertAstraGeometry(const CVolumeGeometry2D* pVolGeom,
+ const CProjectionGeometry2D* pProjGeom,
+ astraCUDA::SParProjection*& pParProjs,
+ astraCUDA::SFanProjection*& pFanProjs,
+ float& outputScale)
+{
+ const CParallelProjectionGeometry2D* parProjGeom = dynamic_cast<const CParallelProjectionGeometry2D*>(pProjGeom);
+ const CParallelVecProjectionGeometry2D* parVecProjGeom = dynamic_cast<const CParallelVecProjectionGeometry2D*>(pProjGeom);
+ const CFanFlatProjectionGeometry2D* fanProjGeom = dynamic_cast<const CFanFlatProjectionGeometry2D*>(pProjGeom);
+ const CFanFlatVecProjectionGeometry2D* fanVecProjGeom = dynamic_cast<const CFanFlatVecProjectionGeometry2D*>(pProjGeom);
+
+ bool ok = false;
+
+ if (parProjGeom) {
+ ok = convertAstraGeometry(pVolGeom, parProjGeom, pParProjs, outputScale);
+ } else if (parVecProjGeom) {
+ ok = convertAstraGeometry(pVolGeom, parVecProjGeom, pParProjs, outputScale);
+ } else if (fanProjGeom) {
+ ok = convertAstraGeometry(pVolGeom, fanProjGeom, pFanProjs, outputScale);
+ } else if (fanVecProjGeom) {
+ ok = convertAstraGeometry(pVolGeom, fanVecProjGeom, pFanProjs, outputScale);
+ } else {
+ ok = false;
+ }
+
+ return ok;
+}
+
+bool convertAstraGeometry_dims(const CVolumeGeometry2D* pVolGeom,
+ const CProjectionGeometry2D* pProjGeom,
+ SDimensions& dims)
+{
+ dims.iVolWidth = pVolGeom->getGridColCount();
+ dims.iVolHeight = pVolGeom->getGridRowCount();
+
+ dims.iProjAngles = pProjGeom->getProjectionAngleCount();
+ dims.iProjDets = pProjGeom->getDetectorCount();
+
+ dims.iRaysPerDet = 1;
+ dims.iRaysPerPixelDim = 1;
+
+ return true;
+}
+
+
}
diff --git a/cuda/2d/astra.h b/cuda/2d/astra.h
index e4cefac..9355cb4 100644
--- a/cuda/2d/astra.h
+++ b/cuda/2d/astra.h
@@ -41,140 +41,12 @@ enum Cuda2DProjectionKernel {
};
class CParallelProjectionGeometry2D;
+class CParallelVecProjectionGeometry2D;
class CFanFlatProjectionGeometry2D;
class CFanFlatVecProjectionGeometry2D;
class CVolumeGeometry2D;
+class CProjectionGeometry2D;
-class AstraFBP_internal;
-
-class _AstraExport AstraFBP {
-public:
- // Constructor
- AstraFBP();
-
- // Destructor
- ~AstraFBP();
-
- // Set the size of the reconstruction rectangle.
- // Volume pixels are currently assumed to be 1x1 squares.
- bool setReconstructionGeometry(unsigned int iVolWidth,
- unsigned int iVolHeight,
- float fPixelSize = 1.0f);
-
- // Set the projection angles and number of detector pixels per angle.
- // pfAngles must be a float array of length iProjAngles.
- // fDetSize indicates the size of a detector pixel compared to a
- // volume pixel edge.
- //
- // pfAngles will only be read from during this call.
- bool setProjectionGeometry(unsigned int iProjAngles,
- unsigned int iProjDets,
- const float *pfAngles,
- float fDetSize = 1.0f);
- // Set the projection angles and number of detector pixels per angle.
- // pfAngles must be a float array of length iProjAngles.
- // fDetSize indicates the size of a detector pixel compared to a
- // volume pixel edge.
- //
- // pfAngles, fanProjs will only be read from during this call.
- bool setFanGeometry(unsigned int iProjAngles,
- unsigned int iProjDets,
- const astraCUDA::SFanProjection *fanProjs,
- const float *pfAngles,
- float fOriginSourceDistance,
- float fOriginDetectorDistance,
- float fDetSize = 1.0f,
- bool bShortScan = false);
-
- // Set linear supersampling factor for the BP.
- // (The number of rays is the square of this)
- //
- // This may optionally be called before init().
- bool setPixelSuperSampling(unsigned int iPixelSuperSampling);
-
- // Set per-detector shifts.
- //
- // pfTOffsets will only be read from during this call.
- bool setTOffsets(const float *pfTOffsets);
-
- // Returns the required size of a filter in the fourier domain
- // when multiplying it with the fft of the projection data.
- // Its value is equal to the smallest power of two larger than
- // or equal to twice the number of detectors in the spatial domain.
- //
- // _iDetectorCount is the number of detectors in the spatial domain.
- static int calcFourierFilterSize(int _iDetectorCount);
-
- // Sets the filter type. Some filter types require the user to supply an
- // array containing the filter.
- // The number of elements in a filter in the fourier domain should be equal
- // to the value returned by calcFourierFilterSize().
- // The following types require a filter:
- //
- // - FILTER_PROJECTION:
- // The filter size should be equal to the output of
- // calcFourierFilterSize(). The filtered sinogram is
- // multiplied with the supplied filter.
- //
- // - FILTER_SINOGRAM:
- // Same as FILTER_PROJECTION, but now the filter should contain a row for
- // every projection direction.
- //
- // - FILTER_RPROJECTION:
- // The filter should now contain one kernel (= ifft of filter), with the
- // peak in the center. The filter width
- // can be any value. If odd, the peak is assumed to be in the center, if
- // even, it is assumed to be at floor(filter-width/2).
- //
- // - FILTER_RSINOGRAM
- // Same as FILTER_RPROJECTION, but now the supplied filter should contain a
- // row for every projection direction.
- //
- // A large number of other filters (FILTER_RAMLAK, FILTER_SHEPPLOGAN,
- // FILTER_COSINE, FILTER_HAMMING, and FILTER_HANN)
- // have a D variable, which gives the cutoff point in the frequency domain.
- // Setting this value to 1.0 will include the whole filter
- bool setFilter(E_FBPFILTER _eFilter,
- const float * _pfHostFilter = NULL,
- int _iFilterWidth = 0, float _fD = 1.0f, float _fFilterParameter = -1.0f);
-
- // Initialize CUDA, allocate GPU buffers and
- // precompute geometry-specific data.
- //
- // CUDA is set up to use GPU number iGPUIndex.
- //
- // This must be called after calling setReconstructionGeometry() and
- // setProjectionGeometry().
- bool init(int iGPUIndex = 0);
-
- // Setup input sinogram for a slice.
- // pfSinogram must be a float array of size iProjAngles*iSinogramPitch.
- // NB: iSinogramPitch is measured in floats, not in bytes.
- //
- // This must be called after init(), and before iterate(). It may be
- // called again after iterate()/getReconstruction() to start a new slice.
- //
- // pfSinogram will only be read from during this call.
- bool setSinogram(const float* pfSinogram, unsigned int iSinogramPitch);
-
- // Runs an FBP reconstruction.
- // This must be called after setSinogram().
- //
- // run can be called before setFilter, but will then use the default Ram-Lak filter
- bool run();
-
- // Get the reconstructed slice.
- // pfReconstruction must be a float array of size
- // iVolHeight*iReconstructionPitch.
- // NB: iReconstructionPitch is measured in floats, not in bytes.
- //
- // This may be called after run().
- bool getReconstruction(float* pfReconstruction,
- unsigned int iReconstructionPitch) const;
-
-private:
- AstraFBP_internal* pData;
-};
class _AstraExport BPalgo : public astraCUDA::ReconAlgo {
public:
@@ -197,8 +69,8 @@ public:
_AstraExport bool astraCudaFP(const float* pfVolume, float* pfSinogram,
unsigned int iVolWidth, unsigned int iVolHeight,
unsigned int iProjAngles, unsigned int iProjDets,
- const float *pfAngles, const float *pfOffsets,
- float fDetSize = 1.0f, unsigned int iDetSuperSampling = 1,
+ const SParProjection *pAngles,
+ unsigned int iDetSuperSampling = 1,
float fOutputScale = 1.0f, int iGPUIndex = 0);
_AstraExport bool astraCudaFanFP(const float* pfVolume, float* pfSinogram,
@@ -211,8 +83,14 @@ _AstraExport bool astraCudaFanFP(const float* pfVolume, float* pfSinogram,
_AstraExport bool convertAstraGeometry(const CVolumeGeometry2D* pVolGeom,
const CParallelProjectionGeometry2D* pProjGeom,
- float*& pfDetectorOffsets, float*& pfProjectionAngles,
- float& fDetSize, float& fOutputScale);
+ astraCUDA::SParProjection*& pProjs,
+ float& fOutputScale);
+
+_AstraExport bool convertAstraGeometry(const CVolumeGeometry2D* pVolGeom,
+ const CParallelVecProjectionGeometry2D* pProjGeom,
+ astraCUDA::SParProjection*& pProjs,
+ float& fOutputScale);
+
_AstraExport bool convertAstraGeometry(const CVolumeGeometry2D* pVolGeom,
const CFanFlatProjectionGeometry2D* pProjGeom,
@@ -224,6 +102,15 @@ _AstraExport bool convertAstraGeometry(const CVolumeGeometry2D* pVolGeom,
astraCUDA::SFanProjection*& pProjs,
float& outputScale);
+_AstraExport bool convertAstraGeometry_dims(const CVolumeGeometry2D* pVolGeom,
+ const CProjectionGeometry2D* pProjGeom,
+ astraCUDA::SDimensions& dims);
+
+_AstraExport bool convertAstraGeometry(const CVolumeGeometry2D* pVolGeom,
+ const CProjectionGeometry2D* pProjGeom,
+ astraCUDA::SParProjection*& pParProjs,
+ astraCUDA::SFanProjection*& pFanProjs,
+ float& outputScale);
}
namespace astraCUDA {
diff --git a/cuda/2d/cgls.cu b/cuda/2d/cgls.cu
index 6962d81..1a31438 100644
--- a/cuda/2d/cgls.cu
+++ b/cuda/2d/cgls.cu
@@ -206,42 +206,6 @@ float CGLS::computeDiffNorm()
return sqrt(s);
}
-bool doCGLS(float* D_volumeData, unsigned int volumePitch,
- float* D_sinoData, unsigned int sinoPitch,
- const SDimensions& dims, /*const SAugmentedData& augs,*/
- const float* angles, const float* TOffsets, unsigned int iterations)
-{
- CGLS cgls;
- bool ok = true;
-
- ok &= cgls.setGeometry(dims, angles);
-#if 0
- if (D_maskData)
- ok &= cgls.enableVolumeMask();
-#endif
- if (TOffsets)
- ok &= cgls.setTOffsets(TOffsets);
-
- if (!ok)
- return false;
-
- ok = cgls.init();
- if (!ok)
- return false;
-
-#if 0
- if (D_maskData)
- ok &= cgls.setVolumeMask(D_maskData, maskPitch);
-#endif
-
- ok &= cgls.setBuffers(D_volumeData, volumePitch, D_sinoData, sinoPitch);
- if (!ok)
- return false;
-
- ok = cgls.iterate(iterations);
-
- return ok;
-}
}
diff --git a/cuda/2d/dims.h b/cuda/2d/dims.h
index afb997a..10eaabf 100644
--- a/cuda/2d/dims.h
+++ b/cuda/2d/dims.h
@@ -33,9 +33,11 @@ along with the ASTRA Toolbox. If not, see <http://www.gnu.org/licenses/>.
namespace astraCUDA {
+using astra::SParProjection;
using astra::SFanProjection;
+
struct SDimensions {
// Width, height of reconstruction volume
unsigned int iVolWidth;
@@ -47,9 +49,6 @@ struct SDimensions {
// Number of detector pixels
unsigned int iProjDets;
- // size of detector compared to volume pixels
- float fDetScale;
-
// in FP, number of rays to trace per detector pixel.
// This should usually be set to 1.
// If fDetScale > 1, this should be set to an integer of roughly
diff --git a/cuda/2d/em.cu b/cuda/2d/em.cu
index 11ed45b..03cc652 100644
--- a/cuda/2d/em.cu
+++ b/cuda/2d/em.cu
@@ -169,34 +169,6 @@ float EM::computeDiffNorm()
}
-bool doEM(float* D_volumeData, unsigned int volumePitch,
- float* D_sinoData, unsigned int sinoPitch,
- const SDimensions& dims, const float* angles,
- const float* TOffsets, unsigned int iterations)
-{
- EM em;
- bool ok = true;
-
- ok &= em.setGeometry(dims, angles);
- if (TOffsets)
- ok &= em.setTOffsets(TOffsets);
-
- if (!ok)
- return false;
-
- ok = em.init();
- if (!ok)
- return false;
-
- ok &= em.setBuffers(D_volumeData, volumePitch, D_sinoData, sinoPitch);
- if (!ok)
- return false;
-
- ok = em.iterate(iterations);
-
- return ok;
-}
-
}
#ifdef STANDALONE
diff --git a/cuda/2d/fbp.cu b/cuda/2d/fbp.cu
new file mode 100644
index 0000000..365fc88
--- /dev/null
+++ b/cuda/2d/fbp.cu
@@ -0,0 +1,347 @@
+/*
+-----------------------------------------------------------------------
+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 "fbp.h"
+#include "fft.h"
+#include "par_bp.h"
+#include "fan_bp.h"
+#include "util.h"
+
+// For fan-beam preweighting
+#include "../3d/fdk.h"
+
+#include "astra/Logging.h"
+
+#include <cuda.h>
+
+namespace astraCUDA {
+
+
+
+static int calcNextPowerOfTwo(int n)
+{
+ int x = 1;
+ while (x < n)
+ x *= 2;
+
+ return x;
+}
+
+// static
+int FBP::calcFourierFilterSize(int _iDetectorCount)
+{
+ int iFFTRealDetCount = calcNextPowerOfTwo(2 * _iDetectorCount);
+ int iFreqBinCount = calcFFTFourierSize(iFFTRealDetCount);
+
+ // CHECKME: Matlab makes this at least 64. Do we also need to?
+ return iFreqBinCount;
+}
+
+
+
+
+FBP::FBP() : ReconAlgo()
+{
+ D_filter = 0;
+
+}
+
+FBP::~FBP()
+{
+ reset();
+}
+
+void FBP::reset()
+{
+ if (D_filter) {
+ freeComplexOnDevice((cufftComplex *)D_filter);
+ D_filter = 0;
+ }
+}
+
+bool FBP::init()
+{
+ return true;
+}
+
+bool FBP::setFilter(astra::E_FBPFILTER _eFilter, const float * _pfHostFilter /* = NULL */, int _iFilterWidth /* = 0 */, float _fD /* = 1.0f */, float _fFilterParameter /* = -1.0f */)
+{
+ if (D_filter)
+ {
+ freeComplexOnDevice((cufftComplex*)D_filter);
+ D_filter = 0;
+ }
+
+ if (_eFilter == astra::FILTER_NONE)
+ return true; // leave D_filter set to 0
+
+
+ int iFFTRealDetCount = calcNextPowerOfTwo(2 * dims.iProjDets);
+ int iFreqBinCount = calcFFTFourierSize(iFFTRealDetCount);
+
+ cufftComplex * pHostFilter = new cufftComplex[dims.iProjAngles * iFreqBinCount];
+ memset(pHostFilter, 0, sizeof(cufftComplex) * dims.iProjAngles * iFreqBinCount);
+
+ allocateComplexOnDevice(dims.iProjAngles, iFreqBinCount, (cufftComplex**)&D_filter);
+
+ switch(_eFilter)
+ {
+ case astra::FILTER_NONE:
+ // handled above
+ break;
+ case astra::FILTER_RAMLAK:
+ case astra::FILTER_SHEPPLOGAN:
+ case astra::FILTER_COSINE:
+ case astra::FILTER_HAMMING:
+ case astra::FILTER_HANN:
+ case astra::FILTER_TUKEY:
+ case astra::FILTER_LANCZOS:
+ case astra::FILTER_TRIANGULAR:
+ case astra::FILTER_GAUSSIAN:
+ case astra::FILTER_BARTLETTHANN:
+ case astra::FILTER_BLACKMAN:
+ case astra::FILTER_NUTTALL:
+ case astra::FILTER_BLACKMANHARRIS:
+ case astra::FILTER_BLACKMANNUTTALL:
+ case astra::FILTER_FLATTOP:
+ case astra::FILTER_PARZEN:
+ {
+ genFilter(_eFilter, _fD, dims.iProjAngles, pHostFilter, iFFTRealDetCount, iFreqBinCount, _fFilterParameter);
+ uploadComplexArrayToDevice(dims.iProjAngles, iFreqBinCount, pHostFilter, (cufftComplex*)D_filter);
+
+ break;
+ }
+ case astra::FILTER_PROJECTION:
+ {
+ // make sure the offered filter has the correct size
+ assert(_iFilterWidth == iFreqBinCount);
+
+ for(int iFreqBinIndex = 0; iFreqBinIndex < iFreqBinCount; iFreqBinIndex++)
+ {
+ float fValue = _pfHostFilter[iFreqBinIndex];
+
+ for(int iProjectionIndex = 0; iProjectionIndex < (int)dims.iProjAngles; iProjectionIndex++)
+ {
+ pHostFilter[iFreqBinIndex + iProjectionIndex * iFreqBinCount].x = fValue;
+ pHostFilter[iFreqBinIndex + iProjectionIndex * iFreqBinCount].y = 0.0f;
+ }
+ }
+ uploadComplexArrayToDevice(dims.iProjAngles, iFreqBinCount, pHostFilter, (cufftComplex*)D_filter);
+ break;
+ }
+ case astra::FILTER_SINOGRAM:
+ {
+ // make sure the offered filter has the correct size
+ assert(_iFilterWidth == iFreqBinCount);
+
+ for(int iFreqBinIndex = 0; iFreqBinIndex < iFreqBinCount; iFreqBinIndex++)
+ {
+ for(int iProjectionIndex = 0; iProjectionIndex < (int)dims.iProjAngles; iProjectionIndex++)
+ {
+ float fValue = _pfHostFilter[iFreqBinIndex + iProjectionIndex * _iFilterWidth];
+
+ pHostFilter[iFreqBinIndex + iProjectionIndex * iFreqBinCount].x = fValue;
+ pHostFilter[iFreqBinIndex + iProjectionIndex * iFreqBinCount].y = 0.0f;
+ }
+ }
+ uploadComplexArrayToDevice(dims.iProjAngles, iFreqBinCount, pHostFilter, (cufftComplex*)D_filter);
+ break;
+ }
+ case astra::FILTER_RPROJECTION:
+ {
+ int iProjectionCount = dims.iProjAngles;
+ int iRealFilterElementCount = iProjectionCount * iFFTRealDetCount;
+ float * pfHostRealFilter = new float[iRealFilterElementCount];
+ memset(pfHostRealFilter, 0, sizeof(float) * iRealFilterElementCount);
+
+ int iUsedFilterWidth = min(_iFilterWidth, iFFTRealDetCount);
+ int iStartFilterIndex = (_iFilterWidth - iUsedFilterWidth) / 2;
+ int iMaxFilterIndex = iStartFilterIndex + iUsedFilterWidth;
+
+ int iFilterShiftSize = _iFilterWidth / 2;
+
+ for(int iDetectorIndex = iStartFilterIndex; iDetectorIndex < iMaxFilterIndex; iDetectorIndex++)
+ {
+ int iFFTInFilterIndex = (iDetectorIndex + iFFTRealDetCount - iFilterShiftSize) % iFFTRealDetCount;
+ float fValue = _pfHostFilter[iDetectorIndex];
+
+ for(int iProjectionIndex = 0; iProjectionIndex < (int)dims.iProjAngles; iProjectionIndex++)
+ {
+ pfHostRealFilter[iFFTInFilterIndex + iProjectionIndex * iFFTRealDetCount] = fValue;
+ }
+ }
+
+ float* pfDevRealFilter = NULL;
+ cudaMalloc((void **)&pfDevRealFilter, sizeof(float) * iRealFilterElementCount); // TODO: check for errors
+ cudaMemcpy(pfDevRealFilter, pfHostRealFilter, sizeof(float) * iRealFilterElementCount, cudaMemcpyHostToDevice);
+ delete[] pfHostRealFilter;
+
+ runCudaFFT(iProjectionCount, pfDevRealFilter, iFFTRealDetCount, iFFTRealDetCount, iFFTRealDetCount, iFreqBinCount, (cufftComplex*)D_filter);
+
+ cudaFree(pfDevRealFilter);
+
+ break;
+ }
+ case astra::FILTER_RSINOGRAM:
+ {
+ int iProjectionCount = dims.iProjAngles;
+ int iRealFilterElementCount = iProjectionCount * iFFTRealDetCount;
+ float* pfHostRealFilter = new float[iRealFilterElementCount];
+ memset(pfHostRealFilter, 0, sizeof(float) * iRealFilterElementCount);
+
+ int iUsedFilterWidth = min(_iFilterWidth, iFFTRealDetCount);
+ int iStartFilterIndex = (_iFilterWidth - iUsedFilterWidth) / 2;
+ int iMaxFilterIndex = iStartFilterIndex + iUsedFilterWidth;
+
+ int iFilterShiftSize = _iFilterWidth / 2;
+
+ for(int iDetectorIndex = iStartFilterIndex; iDetectorIndex < iMaxFilterIndex; iDetectorIndex++)
+ {
+ int iFFTInFilterIndex = (iDetectorIndex + iFFTRealDetCount - iFilterShiftSize) % iFFTRealDetCount;
+
+ for(int iProjectionIndex = 0; iProjectionIndex < (int)dims.iProjAngles; iProjectionIndex++)
+ {
+ float fValue = _pfHostFilter[iDetectorIndex + iProjectionIndex * _iFilterWidth];
+ pfHostRealFilter[iFFTInFilterIndex + iProjectionIndex * iFFTRealDetCount] = fValue;
+ }
+ }
+
+ float* pfDevRealFilter = NULL;
+ cudaMalloc((void **)&pfDevRealFilter, sizeof(float) * iRealFilterElementCount); // TODO: check for errors
+ cudaMemcpy(pfDevRealFilter, pfHostRealFilter, sizeof(float) * iRealFilterElementCount, cudaMemcpyHostToDevice);
+ delete[] pfHostRealFilter;
+
+ runCudaFFT(iProjectionCount, pfDevRealFilter, iFFTRealDetCount, iFFTRealDetCount, iFFTRealDetCount, iFreqBinCount, (cufftComplex*)D_filter);
+
+ cudaFree(pfDevRealFilter);
+
+ break;
+ }
+ default:
+ {
+ ASTRA_ERROR("FBP::setFilter: Unknown filter type requested");
+ delete [] pHostFilter;
+ return false;
+ }
+ }
+
+ delete [] pHostFilter;
+
+ return true;
+}
+
+bool FBP::iterate(unsigned int iterations)
+{
+ zeroVolumeData(D_volumeData, volumePitch, dims);
+
+ bool ok = false;
+
+ if (fanProjs) {
+ // Call FDK_PreWeight to handle fan beam geometry. We treat
+ // this as a cone beam setup of a single slice:
+
+ // TODO: TOffsets affects this preweighting...
+
+ // TODO: We take the fan parameters from the last projection here
+ // without checking if they're the same in all projections
+
+ float *pfAngles = new float[dims.iProjAngles];
+
+ float fOriginSource, fOriginDetector, fDetSize, fOffset;
+ for (unsigned int i = 0; i < dims.iProjAngles; ++i) {
+ bool ok = astra::getFanParameters(fanProjs[i], dims.iProjDets,
+ pfAngles[i],
+ fOriginSource, fOriginDetector,
+ fDetSize, fOffset);
+ if (!ok) {
+ ASTRA_ERROR("FBP_CUDA: Failed to extract circular fan beam parameters from fan beam geometry");
+ return false;
+ }
+ }
+
+ // We create a fake cudaPitchedPtr
+ cudaPitchedPtr tmp;
+ tmp.ptr = D_sinoData;
+ tmp.pitch = sinoPitch * sizeof(float);
+ tmp.xsize = dims.iProjDets;
+ tmp.ysize = dims.iProjAngles;
+ // and a fake Dimensions3D
+ astraCUDA3d::SDimensions3D dims3d;
+ dims3d.iVolX = dims.iVolWidth;
+ dims3d.iVolY = dims.iVolHeight;
+ dims3d.iVolZ = 1;
+ dims3d.iProjAngles = dims.iProjAngles;
+ dims3d.iProjU = dims.iProjDets;
+ dims3d.iProjV = 1;
+
+ astraCUDA3d::FDK_PreWeight(tmp, fOriginSource,
+ fOriginDetector, 0.0f,
+ fDetSize, 1.0f,
+ m_bShortScan, dims3d, pfAngles);
+ } else {
+ // TODO: How should different detector pixel size in different
+ // projections be handled?
+ }
+
+ if (D_filter) {
+
+ int iFFTRealDetCount = calcNextPowerOfTwo(2 * dims.iProjDets);
+ int iFFTFourDetCount = calcFFTFourierSize(iFFTRealDetCount);
+
+ cufftComplex * pDevComplexSinogram = NULL;
+
+ allocateComplexOnDevice(dims.iProjAngles, iFFTFourDetCount, &pDevComplexSinogram);
+
+ runCudaFFT(dims.iProjAngles, D_sinoData, sinoPitch, dims.iProjDets, iFFTRealDetCount, iFFTFourDetCount, pDevComplexSinogram);
+
+ applyFilter(dims.iProjAngles, iFFTFourDetCount, pDevComplexSinogram, (cufftComplex*)D_filter);
+
+ runCudaIFFT(dims.iProjAngles, pDevComplexSinogram, D_sinoData, sinoPitch, dims.iProjDets, iFFTRealDetCount, iFFTFourDetCount);
+
+ freeComplexOnDevice(pDevComplexSinogram);
+
+ }
+
+ float fOutputScale = (M_PI / 2.0f) / (float)dims.iProjAngles;
+
+ if (fanProjs) {
+ ok = FanBP_FBPWeighted(D_volumeData, volumePitch, D_sinoData, sinoPitch, dims, fanProjs, fOutputScale);
+
+ } else {
+ ok = BP(D_volumeData, volumePitch, D_sinoData, sinoPitch, dims, parProjs, fOutputScale);
+ }
+ if(!ok)
+ {
+ return false;
+ }
+
+ return true;
+}
+
+
+}
diff --git a/cuda/2d/fbp.h b/cuda/2d/fbp.h
new file mode 100644
index 0000000..8b4d64d
--- /dev/null
+++ b/cuda/2d/fbp.h
@@ -0,0 +1,98 @@
+/*
+-----------------------------------------------------------------------
+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 "algo.h"
+#include "fbp_filters.h"
+
+namespace astraCUDA {
+
+class _AstraExport FBP : public ReconAlgo {
+public:
+ FBP();
+ ~FBP();
+
+ virtual bool useSinogramMask() { return false; }
+ virtual bool useVolumeMask() { return false; }
+
+ // Returns the required size of a filter in the fourier domain
+ // when multiplying it with the fft of the projection data.
+ // Its value is equal to the smallest power of two larger than
+ // or equal to twice the number of detectors in the spatial domain.
+ //
+ // _iDetectorCount is the number of detectors in the spatial domain.
+ static int calcFourierFilterSize(int _iDetectorCount);
+
+ // Sets the filter type. Some filter types require the user to supply an
+ // array containing the filter.
+ // The number of elements in a filter in the fourier domain should be equal
+ // to the value returned by calcFourierFilterSize().
+ // The following types require a filter:
+ //
+ // - FILTER_PROJECTION:
+ // The filter size should be equal to the output of
+ // calcFourierFilterSize(). The filtered sinogram is
+ // multiplied with the supplied filter.
+ //
+ // - FILTER_SINOGRAM:
+ // Same as FILTER_PROJECTION, but now the filter should contain a row for
+ // every projection direction.
+ //
+ // - FILTER_RPROJECTION:
+ // The filter should now contain one kernel (= ifft of filter), with the
+ // peak in the center. The filter width
+ // can be any value. If odd, the peak is assumed to be in the center, if
+ // even, it is assumed to be at floor(filter-width/2).
+ //
+ // - FILTER_RSINOGRAM
+ // Same as FILTER_RPROJECTION, but now the supplied filter should contain a
+ // row for every projection direction.
+ //
+ // A large number of other filters (FILTER_RAMLAK, FILTER_SHEPPLOGAN,
+ // FILTER_COSINE, FILTER_HAMMING, and FILTER_HANN)
+ // have a D variable, which gives the cutoff point in the frequency domain.
+ // Setting this value to 1.0 will include the whole filter
+ bool setFilter(astra::E_FBPFILTER _eFilter,
+ const float * _pfHostFilter = NULL,
+ int _iFilterWidth = 0, float _fD = 1.0f, float _fFilterParameter = -1.0f);
+
+ bool setShortScan(bool ss) { m_bShortScan = ss; return true; }
+
+ virtual bool init();
+
+ virtual bool iterate(unsigned int iterations);
+
+ virtual float computeDiffNorm() { return 0.0f; } // TODO
+
+protected:
+ void reset();
+
+ void* D_filter; // cufftComplex*
+ bool m_bShortScan;
+};
+
+}
diff --git a/cuda/2d/fbp_filters.h b/cuda/2d/fbp_filters.h
index f1223c5..bf1342d 100644
--- a/cuda/2d/fbp_filters.h
+++ b/cuda/2d/fbp_filters.h
@@ -28,6 +28,8 @@ along with the ASTRA Toolbox. If not, see <http://www.gnu.org/licenses/>.
#ifndef FBP_FILTERS_H
#define FBP_FILTERS_H
+namespace astra {
+
enum E_FBPFILTER
{
FILTER_NONE, //< no filter (regular BP)
@@ -54,4 +56,6 @@ enum E_FBPFILTER
FILTER_RSINOGRAM, //< sinogram filter in real space
};
+}
+
#endif /* FBP_FILTERS_H */
diff --git a/cuda/2d/fft.cu b/cuda/2d/fft.cu
index 654bbae..5500e14 100644
--- a/cuda/2d/fft.cu
+++ b/cuda/2d/fft.cu
@@ -63,6 +63,8 @@ using namespace astra;
} } while (0)
+namespace astraCUDA {
+
__global__ static void applyFilter_kernel(int _iProjectionCount,
int _iFreqBinCount,
cufftComplex * _pSinogram,
@@ -275,11 +277,11 @@ bool runCudaIFFT(int _iProjectionCount, const cufftComplex* _pDevSourceComplex,
// Because the input is real, the Fourier transform is symmetric.
// CUFFT only outputs the first half (ignoring the redundant second half),
// and expects the same as input for the IFFT.
-int calcFFTFourSize(int _iFFTRealSize)
+int calcFFTFourierSize(int _iFFTRealSize)
{
- int iFFTFourSize = _iFFTRealSize / 2 + 1;
+ int iFFTFourierSize = _iFFTRealSize / 2 + 1;
- return iFFTFourSize;
+ return iFFTFourierSize;
}
void genIdenFilter(int _iProjectionCount, cufftComplex * _pFilter,
@@ -694,6 +696,10 @@ void genFilter(E_FBPFILTER _eFilter, float _fD, int _iProjectionCount,
delete[] pfW;
}
+
+}
+
+
#ifdef STANDALONE
__global__ static void doubleFourierOutput_kernel(int _iProjectionCount,
diff --git a/cuda/2d/fft.h b/cuda/2d/fft.h
index 91f05f2..3e91043 100644
--- a/cuda/2d/fft.h
+++ b/cuda/2d/fft.h
@@ -33,6 +33,8 @@ along with the ASTRA Toolbox. If not, see <http://www.gnu.org/licenses/>.
#include "fbp_filters.h"
+namespace astraCUDA {
+
bool allocateComplexOnDevice(int _iProjectionCount,
int _iDetectorCount,
cufftComplex ** _ppDevComplex);
@@ -56,13 +58,15 @@ bool runCudaIFFT(int _iProjectionCount, const cufftComplex* _pDevSourceComplex,
void applyFilter(int _iProjectionCount, int _iFreqBinCount,
cufftComplex * _pSinogram, cufftComplex * _pFilter);
-int calcFFTFourSize(int _iFFTRealSize);
+int calcFFTFourierSize(int _iFFTRealSize);
-void genFilter(E_FBPFILTER _eFilter, float _fD, int _iProjectionCount,
+void genFilter(astra::E_FBPFILTER _eFilter, float _fD, int _iProjectionCount,
cufftComplex * _pFilter, int _iFFTRealDetectorCount,
int _iFFTFourierDetectorCount, float _fParameter = -1.0f);
void genIdenFilter(int _iProjectionCount, cufftComplex * _pFilter,
int _iFFTRealDetectorCount, int _iFFTFourierDetectorCount);
+}
+
#endif /* FFT_H */
diff --git a/cuda/2d/par_bp.cu b/cuda/2d/par_bp.cu
index dfdd193..307c561 100644
--- a/cuda/2d/par_bp.cu
+++ b/cuda/2d/par_bp.cu
@@ -52,8 +52,8 @@ const unsigned int g_blockSlices = 16;
const unsigned int g_MaxAngles = 2560;
-__constant__ float gC_angle_sin[g_MaxAngles];
-__constant__ float gC_angle_cos[g_MaxAngles];
+__constant__ float gC_angle_scaled_sin[g_MaxAngles];
+__constant__ float gC_angle_scaled_cos[g_MaxAngles];
__constant__ float gC_angle_offset[g_MaxAngles];
static bool bindProjDataTexture(float* data, unsigned int pitch, unsigned int width, unsigned int height, cudaTextureAddressMode mode = cudaAddressModeBorder)
@@ -72,7 +72,7 @@ static bool bindProjDataTexture(float* data, unsigned int pitch, unsigned int wi
return true;
}
-__global__ void devBP(float* D_volData, unsigned int volPitch, unsigned int startAngle, bool offsets, const SDimensions dims, float fOutputScale)
+__global__ void devBP(float* D_volData, unsigned int volPitch, unsigned int startAngle, const SDimensions dims, float fOutputScale)
{
const int relX = threadIdx.x;
const int relY = threadIdx.y;
@@ -86,47 +86,30 @@ __global__ void devBP(float* D_volData, unsigned int volPitch, unsigned int star
if (X >= dims.iVolWidth || Y >= dims.iVolHeight)
return;
- const float fX = ( X - 0.5f*dims.iVolWidth + 0.5f ) / dims.fDetScale;
- const float fY = ( Y - 0.5f*dims.iVolHeight + 0.5f ) / dims.fDetScale;
+ const float fX = ( X - 0.5f*dims.iVolWidth + 0.5f );
+ const float fY = ( Y - 0.5f*dims.iVolHeight + 0.5f );
float* volData = (float*)D_volData;
float fVal = 0.0f;
float fA = startAngle + 0.5f;
- const float fT_base = 0.5f*dims.iProjDets - 0.5f + 0.5f;
- if (offsets) {
-
- for (int angle = startAngle; angle < endAngle; ++angle)
- {
- const float cos_theta = gC_angle_cos[angle];
- const float sin_theta = gC_angle_sin[angle];
- const float TOffset = gC_angle_offset[angle];
-
- const float fT = fT_base + fX * cos_theta - fY * sin_theta + TOffset;
- fVal += tex2D(gT_projTexture, fT, fA);
- fA += 1.0f;
- }
-
- } else {
-
- for (int angle = startAngle; angle < endAngle; ++angle)
- {
- const float cos_theta = gC_angle_cos[angle];
- const float sin_theta = gC_angle_sin[angle];
-
- const float fT = fT_base + fX * cos_theta - fY * sin_theta;
- fVal += tex2D(gT_projTexture, fT, fA);
- fA += 1.0f;
- }
+ for (int angle = startAngle; angle < endAngle; ++angle)
+ {
+ const float scaled_cos_theta = gC_angle_scaled_cos[angle];
+ const float scaled_sin_theta = gC_angle_scaled_sin[angle];
+ const float TOffset = gC_angle_offset[angle];
+ const float fT = fX * scaled_cos_theta - fY * scaled_sin_theta + TOffset;
+ fVal += tex2D(gT_projTexture, fT, fA);
+ fA += 1.0f;
}
volData[Y*volPitch+X] += fVal * fOutputScale;
}
// supersampling version
-__global__ void devBP_SS(float* D_volData, unsigned int volPitch, unsigned int startAngle, bool offsets, const SDimensions dims, float fOutputScale)
+__global__ void devBP_SS(float* D_volData, unsigned int volPitch, unsigned int startAngle, const SDimensions dims, float fOutputScale)
{
const int relX = threadIdx.x;
const int relY = threadIdx.y;
@@ -140,61 +123,35 @@ __global__ void devBP_SS(float* D_volData, unsigned int volPitch, unsigned int s
if (X >= dims.iVolWidth || Y >= dims.iVolHeight)
return;
- const float fX = ( X - 0.5f*dims.iVolWidth + 0.5f - 0.5f + 0.5f/dims.iRaysPerPixelDim) / dims.fDetScale;
- const float fY = ( Y - 0.5f*dims.iVolHeight + 0.5f - 0.5f + 0.5f/dims.iRaysPerPixelDim) / dims.fDetScale;
+ const float fX = ( X - 0.5f*dims.iVolWidth + 0.5f - 0.5f + 0.5f/dims.iRaysPerPixelDim);
+ const float fY = ( Y - 0.5f*dims.iVolHeight + 0.5f - 0.5f + 0.5f/dims.iRaysPerPixelDim);
- const float fSubStep = 1.0f/(dims.iRaysPerPixelDim * dims.fDetScale);
+ const float fSubStep = 1.0f/(dims.iRaysPerPixelDim); // * dims.fDetScale);
float* volData = (float*)D_volData;
float fVal = 0.0f;
float fA = startAngle + 0.5f;
- const float fT_base = 0.5f*dims.iProjDets - 0.5f + 0.5f;
fOutputScale /= (dims.iRaysPerPixelDim * dims.iRaysPerPixelDim);
- if (offsets) {
-
- for (int angle = startAngle; angle < endAngle; ++angle)
- {
- const float cos_theta = gC_angle_cos[angle];
- const float sin_theta = gC_angle_sin[angle];
- const float TOffset = gC_angle_offset[angle];
-
- float fT = fT_base + fX * cos_theta - fY * sin_theta + TOffset;
-
- for (int iSubX = 0; iSubX < dims.iRaysPerPixelDim; ++iSubX) {
- float fTy = fT;
- fT += fSubStep * cos_theta;
- for (int iSubY = 0; iSubY < dims.iRaysPerPixelDim; ++iSubY) {
- fVal += tex2D(gT_projTexture, fTy, fA);
- fTy -= fSubStep * sin_theta;
- }
- }
- fA += 1.0f;
- }
-
- } else {
+ for (int angle = startAngle; angle < endAngle; ++angle)
+ {
+ const float cos_theta = gC_angle_scaled_cos[angle];
+ const float sin_theta = gC_angle_scaled_sin[angle];
+ const float TOffset = gC_angle_offset[angle];
- for (int angle = startAngle; angle < endAngle; ++angle)
- {
- const float cos_theta = gC_angle_cos[angle];
- const float sin_theta = gC_angle_sin[angle];
+ float fT = fX * cos_theta - fY * sin_theta + TOffset;
- float fT = fT_base + fX * cos_theta - fY * sin_theta;
-
- for (int iSubX = 0; iSubX < dims.iRaysPerPixelDim; ++iSubX) {
- float fTy = fT;
- fT += fSubStep * cos_theta;
- for (int iSubY = 0; iSubY < dims.iRaysPerPixelDim; ++iSubY) {
- fVal += tex2D(gT_projTexture, fTy, fA);
- fTy -= fSubStep * sin_theta;
- }
+ for (int iSubX = 0; iSubX < dims.iRaysPerPixelDim; ++iSubX) {
+ float fTy = fT;
+ fT += fSubStep * cos_theta;
+ for (int iSubY = 0; iSubY < dims.iRaysPerPixelDim; ++iSubY) {
+ fVal += tex2D(gT_projTexture, fTy, fA);
+ fTy -= fSubStep * sin_theta;
}
- fA += 1.0f;
-
}
-
+ fA += 1.0f;
}
volData[Y*volPitch+X] += fVal * fOutputScale;
@@ -211,12 +168,10 @@ __global__ void devBP_SART(float* D_volData, unsigned int volPitch, float offset
if (X >= dims.iVolWidth || Y >= dims.iVolHeight)
return;
- const float fX = ( X - 0.5f*dims.iVolWidth + 0.5f ) / dims.fDetScale;
- const float fY = ( Y - 0.5f*dims.iVolHeight + 0.5f ) / dims.fDetScale;
-
- const float fT_base = 0.5f*dims.iProjDets - 0.5f + 0.5f;
+ const float fX = ( X - 0.5f*dims.iVolWidth + 0.5f );
+ const float fY = ( Y - 0.5f*dims.iVolHeight + 0.5f );
- const float fT = fT_base + fX * angle_cos - fY * angle_sin + offset;
+ const float fT = fX * angle_cos - fY * angle_sin + offset;
const float fVal = tex2D(gT_projTexture, fT, 0.5f);
D_volData[Y*volPitch+X] += fVal * fOutputScale;
@@ -225,32 +180,35 @@ __global__ void devBP_SART(float* D_volData, unsigned int volPitch, float offset
bool BP_internal(float* D_volumeData, unsigned int volumePitch,
float* D_projData, unsigned int projPitch,
- const SDimensions& dims, const float* angles, const float* TOffsets, float fOutputScale)
+ const SDimensions& dims, const SParProjection* angles,
+ float fOutputScale)
{
- // TODO: process angles block by block
assert(dims.iProjAngles <= g_MaxAngles);
- float* angle_sin = new float[dims.iProjAngles];
- float* angle_cos = new float[dims.iProjAngles];
+ float* angle_scaled_sin = new float[dims.iProjAngles];
+ float* angle_scaled_cos = new float[dims.iProjAngles];
+ float* angle_offset = new float[dims.iProjAngles];
bindProjDataTexture(D_projData, projPitch, dims.iProjDets, dims.iProjAngles);
for (unsigned int i = 0; i < dims.iProjAngles; ++i) {
- angle_sin[i] = sinf(angles[i]);
- angle_cos[i] = cosf(angles[i]);
+ double d = angles[i].fDetUX * angles[i].fRayY - angles[i].fDetUY * angles[i].fRayX;
+ angle_scaled_cos[i] = angles[i].fRayY / d;
+ angle_scaled_sin[i] = -angles[i].fRayX / d; // TODO: Check signs
+ angle_offset[i] = (angles[i].fDetSY * angles[i].fRayX - angles[i].fDetSX * angles[i].fRayY) / d;
}
- cudaError_t e1 = cudaMemcpyToSymbol(gC_angle_sin, angle_sin, dims.iProjAngles*sizeof(float), 0, cudaMemcpyHostToDevice);
- cudaError_t e2 = cudaMemcpyToSymbol(gC_angle_cos, angle_cos, dims.iProjAngles*sizeof(float), 0, cudaMemcpyHostToDevice);
+
+ cudaError_t e1 = cudaMemcpyToSymbol(gC_angle_scaled_sin, angle_scaled_sin, dims.iProjAngles*sizeof(float), 0, cudaMemcpyHostToDevice);
+ cudaError_t e2 = cudaMemcpyToSymbol(gC_angle_scaled_cos, angle_scaled_cos, dims.iProjAngles*sizeof(float), 0, cudaMemcpyHostToDevice);
+ cudaError_t e3 = cudaMemcpyToSymbol(gC_angle_offset, angle_offset, dims.iProjAngles*sizeof(float), 0, cudaMemcpyHostToDevice);
assert(e1 == cudaSuccess);
assert(e2 == cudaSuccess);
+ assert(e3 == cudaSuccess);
- if (TOffsets) {
- cudaError_t e3 = cudaMemcpyToSymbol(gC_angle_offset, TOffsets, dims.iProjAngles*sizeof(float), 0, cudaMemcpyHostToDevice);
- assert(e3 == cudaSuccess);
- }
- delete[] angle_sin;
- delete[] angle_cos;
+ delete[] angle_scaled_sin;
+ delete[] angle_scaled_cos;
+ delete[] angle_offset;
dim3 dimBlock(g_blockSlices, g_blockSliceSize);
dim3 dimGrid((dims.iVolWidth+g_blockSlices-1)/g_blockSlices,
@@ -262,9 +220,9 @@ bool BP_internal(float* D_volumeData, unsigned int volumePitch,
for (unsigned int i = 0; i < dims.iProjAngles; i += g_anglesPerBlock) {
if (dims.iRaysPerPixelDim > 1)
- devBP_SS<<<dimGrid, dimBlock, 0, stream>>>(D_volumeData, volumePitch, i, (TOffsets != 0), dims, fOutputScale);
+ devBP_SS<<<dimGrid, dimBlock, 0, stream>>>(D_volumeData, volumePitch, i, dims, fOutputScale);
else
- devBP<<<dimGrid, dimBlock, 0, stream>>>(D_volumeData, volumePitch, i, (TOffsets != 0), dims, fOutputScale);
+ devBP<<<dimGrid, dimBlock, 0, stream>>>(D_volumeData, volumePitch, i, dims, fOutputScale);
}
cudaThreadSynchronize();
@@ -277,7 +235,7 @@ bool BP_internal(float* D_volumeData, unsigned int volumePitch,
bool BP(float* D_volumeData, unsigned int volumePitch,
float* D_projData, unsigned int projPitch,
- const SDimensions& dims, const float* angles, const float* TOffsets, float fOutputScale)
+ const SDimensions& dims, const SParProjection* angles, float fOutputScale)
{
for (unsigned int iAngle = 0; iAngle < dims.iProjAngles; iAngle += g_MaxAngles) {
SDimensions subdims = dims;
@@ -289,9 +247,7 @@ bool BP(float* D_volumeData, unsigned int volumePitch,
bool ret;
ret = BP_internal(D_volumeData, volumePitch,
D_projData + iAngle * projPitch, projPitch,
- subdims, angles + iAngle,
- TOffsets ? TOffsets + iAngle : 0,
- fOutputScale);
+ subdims, angles + iAngle, fOutputScale);
if (!ret)
return false;
}
@@ -302,25 +258,23 @@ bool BP(float* D_volumeData, unsigned int volumePitch,
bool BP_SART(float* D_volumeData, unsigned int volumePitch,
float* D_projData, unsigned int projPitch,
unsigned int angle, const SDimensions& dims,
- const float* angles, const float* TOffsets, float fOutputScale)
+ const SParProjection* angles, float fOutputScale)
{
// Only one angle.
// We need to Clamp to the border pixels instead of to zero, because
// SART weights with ray length.
bindProjDataTexture(D_projData, projPitch, dims.iProjDets, 1, cudaAddressModeClamp);
- float angle_sin = sinf(angles[angle]);
- float angle_cos = cosf(angles[angle]);
-
- float offset = 0.0f;
- if (TOffsets)
- offset = TOffsets[angle];
+ double d = angles[angle].fDetUX * angles[angle].fRayY - angles[angle].fDetUY * angles[angle].fRayX;
+ float angle_scaled_cos = angles[angle].fRayY / d;
+ float angle_scaled_sin = -angles[angle].fRayX / d; // TODO: Check signs
+ float angle_offset = (angles[angle].fDetSY * angles[angle].fRayX - angles[angle].fDetSX * angles[angle].fRayY) / d;
dim3 dimBlock(g_blockSlices, g_blockSliceSize);
dim3 dimGrid((dims.iVolWidth+g_blockSlices-1)/g_blockSlices,
(dims.iVolHeight+g_blockSliceSize-1)/g_blockSliceSize);
- devBP_SART<<<dimGrid, dimBlock>>>(D_volumeData, volumePitch, offset, angle_sin, angle_cos, dims, fOutputScale);
+ devBP_SART<<<dimGrid, dimBlock>>>(D_volumeData, volumePitch, angle_offset, angle_scaled_sin, angle_scaled_cos, dims, fOutputScale);
cudaThreadSynchronize();
cudaTextForceKernelsCompletion();
diff --git a/cuda/2d/par_bp.h b/cuda/2d/par_bp.h
index 0720030..3fa8a28 100644
--- a/cuda/2d/par_bp.h
+++ b/cuda/2d/par_bp.h
@@ -34,13 +34,13 @@ namespace astraCUDA {
_AstraExport bool BP(float* D_volumeData, unsigned int volumePitch,
float* D_projData, unsigned int projPitch,
- const SDimensions& dims, const float* angles,
- const float* TOffsets, float fOutputScale);
+ const SDimensions& dims, const SParProjection* angles,
+ float fOutputScale);
_AstraExport bool BP_SART(float* D_volumeData, unsigned int volumePitch,
float* D_projData, unsigned int projPitch,
unsigned int angle, const SDimensions& dims,
- const float* angles, const float* TOffsets, float fOutputScale);
+ const SParProjection* angles, float fOutputScale);
}
diff --git a/cuda/2d/par_fp.cu b/cuda/2d/par_fp.cu
index f58a643..a0b04ee 100644
--- a/cuda/2d/par_fp.cu
+++ b/cuda/2d/par_fp.cu
@@ -29,6 +29,7 @@ along with the ASTRA Toolbox. If not, see <http://www.gnu.org/licenses/>.
#include <cassert>
#include <iostream>
#include <list>
+#include <cmath>
#include "util.h"
#include "arith.h"
@@ -50,6 +51,7 @@ namespace astraCUDA {
static const unsigned g_MaxAngles = 2560;
__constant__ float gC_angle[g_MaxAngles];
__constant__ float gC_angle_offset[g_MaxAngles];
+__constant__ float gC_angle_detsize[g_MaxAngles];
// optimization parameters
@@ -62,10 +64,6 @@ static const unsigned int g_blockSlices = 64;
#define iPREC_FACTOR 16
-// if necessary, a buffer of zeroes of size g_MaxAngles
-static float* g_pfZeroes = 0;
-
-
static bool bindVolumeDataTexture(float* data, cudaArray*& dataArray, unsigned int pitch, unsigned int width, unsigned int height)
{
cudaChannelFormatDesc channelDesc = cudaCreateChannelDesc<float>();
@@ -88,9 +86,10 @@ static bool bindVolumeDataTexture(float* data, cudaArray*& dataArray, unsigned i
return true;
}
+
// projection for angles that are roughly horizontal
-// theta between 45 and 135 degrees
-__global__ void FPhorizontal(float* D_projData, unsigned int projPitch, unsigned int startSlice, unsigned int startAngle, unsigned int endAngle, int regionOffset, const SDimensions dims, float outputScale)
+// (detector roughly vertical)
+__global__ void FPhorizontal_simple(float* D_projData, unsigned int projPitch, unsigned int startSlice, unsigned int startAngle, unsigned int endAngle, const SDimensions dims, float outputScale)
{
const int relDet = threadIdx.x;
const int relAngle = threadIdx.y;
@@ -105,33 +104,7 @@ __global__ void FPhorizontal(float* D_projData, unsigned int projPitch, unsigned
const float sin_theta = __sinf(theta);
// compute start detector for this block/angle:
- // (The same for all threadIdx.x)
- // -------------------------------------
-
- const int midSlice = startSlice + g_blockSlices / 2;
-
- // ASSUMPTION: fDetScale >= 1.0f
- // problem: detector regions get skipped because slice blocks aren't large
- // enough
- const unsigned int g_blockSliceSize = g_detBlockSize;
-
- // project (midSlice,midRegion) on this thread's detector
-
- const float fBase = 0.5f*dims.iProjDets - 0.5f +
- (
- (midSlice - 0.5f*dims.iVolWidth + 0.5f) * cos_theta
- - (g_blockSliceSize/2 - 0.5f*dims.iVolHeight + 0.5f) * sin_theta
- + gC_angle_offset[angle]
- ) / dims.fDetScale;
- int iBase = (int)floorf(fBase * fPREC_FACTOR);
- int iInc = (int)floorf(g_blockSliceSize * sin_theta / dims.fDetScale * -fPREC_FACTOR);
-
- // ASSUMPTION: 16 > regionOffset / fDetScale
- const int detRegion = (iBase + (blockIdx.y - regionOffset)*iInc + 16*iPREC_FACTOR*g_detBlockSize) / (iPREC_FACTOR * g_detBlockSize) - 16;
- const int detPrevRegion = (iBase + (blockIdx.y - regionOffset - 1)*iInc + 16*iPREC_FACTOR*g_detBlockSize) / (iPREC_FACTOR * g_detBlockSize) - 16;
-
- if (blockIdx.y > 0 && detRegion == detPrevRegion)
- return;
+ const int detRegion = blockIdx.y;
const int detector = detRegion * g_detBlockSize + relDet;
@@ -141,7 +114,7 @@ __global__ void FPhorizontal(float* D_projData, unsigned int projPitch, unsigned
if (detector < 0 || detector >= dims.iProjDets)
return;
- const float fDetStep = -dims.fDetScale / sin_theta;
+ const float fDetStep = -gC_angle_detsize[angle] / sin_theta;
float fSliceStep = cos_theta / sin_theta;
float fDistCorr;
if (sin_theta > 0.0f)
@@ -191,9 +164,10 @@ __global__ void FPhorizontal(float* D_projData, unsigned int projPitch, unsigned
D_projData[angle*projPitch+detector] += fVal * fDistCorr;
}
+
// projection for angles that are roughly vertical
-// theta between 0 and 45, or 135 and 180 degrees
-__global__ void FPvertical(float* D_projData, unsigned int projPitch, unsigned int startSlice, unsigned int startAngle, unsigned int endAngle, int regionOffset, const SDimensions dims, float outputScale)
+// (detector roughly horizontal)
+__global__ void FPvertical_simple(float* D_projData, unsigned int projPitch, unsigned int startSlice, unsigned int startAngle, unsigned int endAngle, const SDimensions dims, float outputScale)
{
const int relDet = threadIdx.x;
const int relAngle = threadIdx.y;
@@ -208,33 +182,7 @@ __global__ void FPvertical(float* D_projData, unsigned int projPitch, unsigned i
const float sin_theta = __sinf(theta);
// compute start detector for this block/angle:
- // (The same for all threadIdx.x)
- // -------------------------------------
-
- const int midSlice = startSlice + g_blockSlices / 2;
-
- // project (midSlice,midRegion) on this thread's detector
-
- // ASSUMPTION: fDetScale >= 1.0f
- // problem: detector regions get skipped because slice blocks aren't large
- // enough
- const unsigned int g_blockSliceSize = g_detBlockSize;
-
- const float fBase = 0.5f*dims.iProjDets - 0.5f +
- (
- (g_blockSliceSize/2 - 0.5f*dims.iVolWidth + 0.5f) * cos_theta
- - (midSlice - 0.5f*dims.iVolHeight + 0.5f) * sin_theta
- + gC_angle_offset[angle]
- ) / dims.fDetScale;
- int iBase = (int)floorf(fBase * fPREC_FACTOR);
- int iInc = (int)floorf(g_blockSliceSize * cos_theta / dims.fDetScale * fPREC_FACTOR);
-
- // ASSUMPTION: 16 > regionOffset / fDetScale
- const int detRegion = (iBase + (blockIdx.y - regionOffset)*iInc + 16*iPREC_FACTOR*g_detBlockSize) / (iPREC_FACTOR * g_detBlockSize) - 16;
- const int detPrevRegion = (iBase + (blockIdx.y - regionOffset-1)*iInc + 16*iPREC_FACTOR*g_detBlockSize) / (iPREC_FACTOR * g_detBlockSize) - 16;
-
- if (blockIdx.y > 0 && detRegion == detPrevRegion)
- return;
+ const int detRegion = blockIdx.y;
const int detector = detRegion * g_detBlockSize + relDet;
@@ -244,7 +192,7 @@ __global__ void FPvertical(float* D_projData, unsigned int projPitch, unsigned i
if (detector < 0 || detector >= dims.iProjDets)
return;
- const float fDetStep = dims.fDetScale / cos_theta;
+ const float fDetStep = gC_angle_detsize[angle] / cos_theta;
float fSliceStep = sin_theta / cos_theta;
float fDistCorr;
if (cos_theta < 0.0f)
@@ -292,183 +240,45 @@ __global__ void FPvertical(float* D_projData, unsigned int projPitch, unsigned i
D_projData[angle*projPitch+detector] += fVal * fDistCorr;
}
-// projection for angles that are roughly horizontal
-// (detector roughly vertical)
-__global__ void FPhorizontal_simple(float* D_projData, unsigned int projPitch, unsigned int startSlice, unsigned int startAngle, unsigned int endAngle, const SDimensions dims, float outputScale)
-{
- const int relDet = threadIdx.x;
- const int relAngle = threadIdx.y;
-
- int angle = startAngle + blockIdx.x * g_anglesPerBlock + relAngle;
-
- if (angle >= endAngle)
- return;
- const float theta = gC_angle[angle];
- const float cos_theta = __cosf(theta);
- const float sin_theta = __sinf(theta);
- // compute start detector for this block/angle:
- const int detRegion = blockIdx.y;
-
- const int detector = detRegion * g_detBlockSize + relDet;
-
- // Now project the part of the ray to angle,detector through
- // slices startSlice to startSlice+g_blockSlices-1
- if (detector < 0 || detector >= dims.iProjDets)
- return;
+// Coordinates of center of detector pixel number t:
+// x = (t - 0.5*nDets + 0.5 - fOffset) * fSize * cos(fAngle)
+// y = - (t - 0.5*nDets + 0.5 - fOffset) * fSize * sin(fAngle)
- const float fDetStep = -dims.fDetScale / sin_theta;
- float fSliceStep = cos_theta / sin_theta;
- float fDistCorr;
- if (sin_theta > 0.0f)
- fDistCorr = -fDetStep;
- else
- fDistCorr = fDetStep;
- fDistCorr *= outputScale;
- float fVal = 0.0f;
- // project detector on slice
- float fP = (detector - 0.5f*dims.iProjDets + 0.5f - gC_angle_offset[angle]) * fDetStep + (startSlice - 0.5f*dims.iVolWidth + 0.5f) * fSliceStep + 0.5f*dims.iVolHeight - 0.5f + 0.5f;
- float fS = startSlice + 0.5f;
- int endSlice = startSlice + g_blockSlices;
- if (endSlice > dims.iVolWidth)
- endSlice = dims.iVolWidth;
-
- if (dims.iRaysPerDet > 1) {
-
- fP += (-0.5f*dims.iRaysPerDet + 0.5f)/dims.iRaysPerDet * fDetStep;
- const float fSubDetStep = fDetStep / dims.iRaysPerDet;
- fDistCorr /= dims.iRaysPerDet;
-
- fSliceStep -= dims.iRaysPerDet * fSubDetStep;
-
- for (int slice = startSlice; slice < endSlice; ++slice)
- {
- for (int iSubT = 0; iSubT < dims.iRaysPerDet; ++iSubT) {
- fVal += tex2D(gT_volumeTexture, fS, fP);
- fP += fSubDetStep;
- }
- fP += fSliceStep;
- fS += 1.0f;
- }
-
- } else {
-
- for (int slice = startSlice; slice < endSlice; ++slice)
- {
- fVal += tex2D(gT_volumeTexture, fS, fP);
- fP += fSliceStep;
- fS += 1.0f;
- }
-
-
- }
-
- D_projData[angle*projPitch+detector] += fVal * fDistCorr;
-}
-
-
-// projection for angles that are roughly vertical
-// (detector roughly horizontal)
-__global__ void FPvertical_simple(float* D_projData, unsigned int projPitch, unsigned int startSlice, unsigned int startAngle, unsigned int endAngle, const SDimensions dims, float outputScale)
+static void convertAndUploadAngles(const SParProjection *projs, unsigned int nth, unsigned int ndets)
{
- const int relDet = threadIdx.x;
- const int relAngle = threadIdx.y;
-
- int angle = startAngle + blockIdx.x * g_anglesPerBlock + relAngle;
-
- if (angle >= endAngle)
- return;
-
- const float theta = gC_angle[angle];
- const float cos_theta = __cosf(theta);
- const float sin_theta = __sinf(theta);
-
- // compute start detector for this block/angle:
- const int detRegion = blockIdx.y;
-
- const int detector = detRegion * g_detBlockSize + relDet;
-
- // Now project the part of the ray to angle,detector through
- // slices startSlice to startSlice+g_blockSlices-1
-
- if (detector < 0 || detector >= dims.iProjDets)
- return;
-
- const float fDetStep = dims.fDetScale / cos_theta;
- float fSliceStep = sin_theta / cos_theta;
- float fDistCorr;
- if (cos_theta < 0.0f)
- fDistCorr = -fDetStep;
- else
- fDistCorr = fDetStep;
- fDistCorr *= outputScale;
-
- float fVal = 0.0f;
- float fP = (detector - 0.5f*dims.iProjDets + 0.5f - gC_angle_offset[angle]) * fDetStep + (startSlice - 0.5f*dims.iVolHeight + 0.5f) * fSliceStep + 0.5f*dims.iVolWidth - 0.5f + 0.5f;
- float fS = startSlice+0.5f;
- int endSlice = startSlice + g_blockSlices;
- if (endSlice > dims.iVolHeight)
- endSlice = dims.iVolHeight;
-
- if (dims.iRaysPerDet > 1) {
-
- fP += (-0.5f*dims.iRaysPerDet + 0.5f)/dims.iRaysPerDet * fDetStep;
- const float fSubDetStep = fDetStep / dims.iRaysPerDet;
- fDistCorr /= dims.iRaysPerDet;
-
- fSliceStep -= dims.iRaysPerDet * fSubDetStep;
-
- for (int slice = startSlice; slice < endSlice; ++slice)
- {
- for (int iSubT = 0; iSubT < dims.iRaysPerDet; ++iSubT) {
- fVal += tex2D(gT_volumeTexture, fP, fS);
- fP += fSubDetStep;
- }
- fP += fSliceStep;
- fS += 1.0f;
- }
-
- } else {
+ float *angles = new float[nth];
+ float *offsets = new float[nth];
+ float *detsizes = new float[nth];
- for (int slice = startSlice; slice < endSlice; ++slice)
- {
- fVal += tex2D(gT_volumeTexture, fP, fS);
- fP += fSliceStep;
- fS += 1.0f;
- }
-
- }
+ for (int i = 0; i < nth; ++i)
+ getParParameters(projs[i], ndets, angles[i], detsizes[i], offsets[i]);
- D_projData[angle*projPitch+detector] += fVal * fDistCorr;
+ cudaMemcpyToSymbol(gC_angle, angles, nth*sizeof(float), 0, cudaMemcpyHostToDevice);
+ cudaMemcpyToSymbol(gC_angle_offset, offsets, nth*sizeof(float), 0, cudaMemcpyHostToDevice);
+ cudaMemcpyToSymbol(gC_angle_detsize, detsizes, nth*sizeof(float), 0, cudaMemcpyHostToDevice);
}
bool FP_simple_internal(float* D_volumeData, unsigned int volumePitch,
float* D_projData, unsigned int projPitch,
- const SDimensions& dims, const float* angles,
- const float* TOffsets, float outputScale)
+ const SDimensions& dims, const SParProjection* angles,
+ float outputScale)
{
- // TODO: load angles into constant memory in smaller blocks
assert(dims.iProjAngles <= g_MaxAngles);
+ assert(angles);
+
cudaArray* D_dataArray;
bindVolumeDataTexture(D_volumeData, D_dataArray, volumePitch, dims.iVolWidth, dims.iVolHeight);
- cudaMemcpyToSymbol(gC_angle, angles, dims.iProjAngles*sizeof(float), 0, cudaMemcpyHostToDevice);
- if (TOffsets) {
- cudaMemcpyToSymbol(gC_angle_offset, TOffsets, dims.iProjAngles*sizeof(float), 0, cudaMemcpyHostToDevice);
- } else {
- if (!g_pfZeroes) {
- g_pfZeroes = new float[g_MaxAngles];
- memset(g_pfZeroes, 0, g_MaxAngles * sizeof(float));
- }
- cudaMemcpyToSymbol(gC_angle_offset, g_pfZeroes, dims.iProjAngles*sizeof(float), 0, cudaMemcpyHostToDevice);
- }
+ convertAndUploadAngles(angles, dims.iProjAngles, dims.iProjDets);
+
dim3 dimBlock(g_detBlockSize, g_anglesPerBlock); // detector block size, angles
@@ -491,7 +301,7 @@ bool FP_simple_internal(float* D_volumeData, unsigned int volumePitch,
// Maybe we should detect corner cases and put them in the optimal
// group of angles.
if (a != dims.iProjAngles)
- vertical = (fabsf(sinf(angles[a])) <= fabsf(cosf(angles[a])));
+ vertical = (fabsf(angles[a].fRayX) <= fabsf(angles[a].fRayY));
if (a == dims.iProjAngles || vertical != blockVertical) {
// block done
@@ -535,8 +345,8 @@ bool FP_simple_internal(float* D_volumeData, unsigned int volumePitch,
bool FP_simple(float* D_volumeData, unsigned int volumePitch,
float* D_projData, unsigned int projPitch,
- const SDimensions& dims, const float* angles,
- const float* TOffsets, float outputScale)
+ const SDimensions& dims, const SParProjection* angles,
+ float outputScale)
{
for (unsigned int iAngle = 0; iAngle < dims.iProjAngles; iAngle += g_MaxAngles) {
SDimensions subdims = dims;
@@ -549,7 +359,7 @@ bool FP_simple(float* D_volumeData, unsigned int volumePitch,
ret = FP_simple_internal(D_volumeData, volumePitch,
D_projData + iAngle * projPitch, projPitch,
subdims, angles + iAngle,
- TOffsets ? TOffsets + iAngle : 0, outputScale);
+ outputScale);
if (!ret)
return false;
}
@@ -558,106 +368,12 @@ bool FP_simple(float* D_volumeData, unsigned int volumePitch,
bool FP(float* D_volumeData, unsigned int volumePitch,
float* D_projData, unsigned int projPitch,
- const SDimensions& dims, const float* angles,
- const float* TOffsets, float outputScale)
+ const SDimensions& dims, const SParProjection* angles,
+ float outputScale)
{
return FP_simple(D_volumeData, volumePitch, D_projData, projPitch,
- dims, angles, TOffsets, outputScale);
+ dims, angles, outputScale);
- // TODO: Fix bug in this non-simple FP with large detscale and TOffsets
-#if 0
-
- // TODO: load angles into constant memory in smaller blocks
- assert(dims.iProjAngles <= g_MaxAngles);
-
- // TODO: compute region size dynamically to resolve these two assumptions
- // ASSUMPTION: 16 > regionOffset / fDetScale
- const unsigned int g_blockSliceSize = g_detBlockSize;
- assert(16 > (g_blockSlices / g_blockSliceSize) / dims.fDetScale);
- // ASSUMPTION: fDetScale >= 1.0f
- assert(dims.fDetScale > 0.9999f);
-
- cudaArray* D_dataArray;
- bindVolumeDataTexture(D_volumeData, D_dataArray, volumePitch, dims.iVolWidth, dims.iVolHeight);
-
- cudaMemcpyToSymbol(gC_angle, angles, dims.iProjAngles*sizeof(float), 0, cudaMemcpyHostToDevice);
-
- if (TOffsets) {
- cudaMemcpyToSymbol(gC_angle_offset, TOffsets, dims.iProjAngles*sizeof(float), 0, cudaMemcpyHostToDevice);
- } else {
- if (!g_pfZeroes) {
- g_pfZeroes = new float[g_MaxAngles];
- memset(g_pfZeroes, 0, g_MaxAngles * sizeof(float));
- }
- cudaMemcpyToSymbol(gC_angle_offset, g_pfZeroes, dims.iProjAngles*sizeof(float), 0, cudaMemcpyHostToDevice);
- }
-
- int regionOffset = g_blockSlices / g_blockSliceSize;
-
- dim3 dimBlock(g_detBlockSize, g_anglesPerBlock); // region size, angles
-
- std::list<cudaStream_t> streams;
-
-
- // Run over all angles, grouping them into groups of the same
- // orientation (roughly horizontal vs. roughly vertical).
- // Start a stream of grids for each such group.
-
- // TODO: Check if it's worth it to store this info instead
- // of recomputing it every FP.
-
- unsigned int blockStart = 0;
- unsigned int blockEnd = 0;
- bool blockVertical = false;
- for (unsigned int a = 0; a <= dims.iProjAngles; ++a) {
- bool vertical;
- // TODO: Having <= instead of < below causes a 5% speedup.
- // Maybe we should detect corner cases and put them in the optimal
- // group of angles.
- if (a != dims.iProjAngles)
- vertical = (fabsf(sinf(angles[a])) <= fabsf(cosf(angles[a])));
- if (a == dims.iProjAngles || vertical != blockVertical) {
- // block done
-
- blockEnd = a;
- if (blockStart != blockEnd) {
- unsigned int length = dims.iVolHeight;
- if (blockVertical)
- length = dims.iVolWidth;
- dim3 dimGrid((blockEnd-blockStart+g_anglesPerBlock-1)/g_anglesPerBlock,
- (length+g_blockSliceSize-1)/g_blockSliceSize+2*regionOffset); // angle blocks, regions
- // TODO: check if we can't immediately
- // destroy the stream after use
- cudaStream_t stream;
- cudaStreamCreate(&stream);
- streams.push_back(stream);
- //printf("angle block: %d to %d, %d\n", blockStart, blockEnd, blockVertical);
- if (!blockVertical)
- for (unsigned int i = 0; i < dims.iVolWidth; i += g_blockSlices)
- FPhorizontal<<<dimGrid, dimBlock, 0, stream>>>(D_projData, projPitch, i, blockStart, blockEnd, regionOffset, dims, outputScale);
- else
- for (unsigned int i = 0; i < dims.iVolHeight; i += g_blockSlices)
- FPvertical<<<dimGrid, dimBlock, 0, stream>>>(D_projData, projPitch, i, blockStart, blockEnd, regionOffset, dims, outputScale);
- }
- blockVertical = vertical;
- blockStart = a;
- }
- }
-
- for (std::list<cudaStream_t>::iterator iter = streams.begin(); iter != streams.end(); ++iter)
- cudaStreamDestroy(*iter);
-
- streams.clear();
-
- cudaThreadSynchronize();
-
- cudaTextForceKernelsCompletion();
-
- cudaFreeArray(D_dataArray);
-
-
- return true;
-#endif
}
diff --git a/cuda/2d/par_fp.h b/cuda/2d/par_fp.h
index 4830c1e..1f0ff69 100644
--- a/cuda/2d/par_fp.h
+++ b/cuda/2d/par_fp.h
@@ -32,8 +32,8 @@ namespace astraCUDA {
_AstraExport bool FP(float* D_volumeData, unsigned int volumePitch,
float* D_projData, unsigned int projPitch,
- const SDimensions& dims, const float* angles,
- const float* TOffsets, float fOutputScale);
+ const SDimensions& dims, const SParProjection* angles,
+ float fOutputScale);
}
diff --git a/cuda/2d/sart.cu b/cuda/2d/sart.cu
index f444b63..3a3c7e6 100644
--- a/cuda/2d/sart.cu
+++ b/cuda/2d/sart.cu
@@ -253,10 +253,10 @@ bool SART::callFP_SART(float* D_volumeData, unsigned int volumePitch,
{
SDimensions d = dims;
d.iProjAngles = 1;
- if (angles) {
+ if (parProjs) {
assert(!fanProjs);
return FP(D_volumeData, volumePitch, D_projData, projPitch,
- d, &angles[angle], TOffsets, outputScale);
+ d, &parProjs[angle], outputScale);
} else {
assert(fanProjs);
return FanFP(D_volumeData, volumePitch, D_projData, projPitch,
@@ -268,10 +268,10 @@ bool SART::callBP_SART(float* D_volumeData, unsigned int volumePitch,
float* D_projData, unsigned int projPitch,
unsigned int angle, float outputScale)
{
- if (angles) {
+ if (parProjs) {
assert(!fanProjs);
return BP_SART(D_volumeData, volumePitch, D_projData, projPitch,
- angle, dims, angles, TOffsets, outputScale);
+ angle, dims, parProjs, outputScale);
} else {
assert(fanProjs);
return FanBP_SART(D_volumeData, volumePitch, D_projData, projPitch,
diff --git a/cuda/2d/sirt.cu b/cuda/2d/sirt.cu
index 0079717..b393d7f 100644
--- a/cuda/2d/sirt.cu
+++ b/cuda/2d/sirt.cu
@@ -151,7 +151,7 @@ bool SIRT::precomputeWeights()
bool SIRT::doSlabCorrections()
{
// This function compensates for effectively infinitely large slab-like
- // objects of finite thickness 1.
+ // objects of finite thickness 1 in a parallel beam geometry.
// Each ray through the object has an intersection of length d/cos(alpha).
// The length of the ray actually intersecting the reconstruction volume is
@@ -169,6 +169,10 @@ bool SIRT::doSlabCorrections()
if (useVolumeMask || useSinogramMask)
return false;
+ // Parallel-beam only
+ if (!parProjs)
+ return false;
+
// multiply by line weights
processSino<opDiv>(D_sinoData, D_lineWeight, projPitch, dims);
@@ -180,7 +184,9 @@ bool SIRT::doSlabCorrections()
float bound = cosf(1.3963f);
float* t = (float*)D_sinoData;
for (int i = 0; i < dims.iProjAngles; ++i) {
- float f = fabs(cosf(angles[i]));
+ float angle, detsize, offset;
+ getParParameters(parProjs[i], dims.iProjDets, angle, detsize, offset);
+ float f = fabs(cosf(angle));
if (f < bound)
f = bound;
@@ -188,7 +194,6 @@ bool SIRT::doSlabCorrections()
processSino<opMul>(t, f, sinoPitch, subdims);
t += sinoPitch;
}
-
return true;
}
@@ -298,40 +303,6 @@ float SIRT::computeDiffNorm()
}
-bool doSIRT(float* D_volumeData, unsigned int volumePitch,
- float* D_sinoData, unsigned int sinoPitch,
- float* D_maskData, unsigned int maskPitch,
- const SDimensions& dims, const float* angles,
- const float* TOffsets, unsigned int iterations)
-{
- SIRT sirt;
- bool ok = true;
-
- ok &= sirt.setGeometry(dims, angles);
- if (D_maskData)
- ok &= sirt.enableVolumeMask();
- if (TOffsets)
- ok &= sirt.setTOffsets(TOffsets);
-
- if (!ok)
- return false;
-
- ok = sirt.init();
- if (!ok)
- return false;
-
- if (D_maskData)
- ok &= sirt.setVolumeMask(D_maskData, maskPitch);
-
- ok &= sirt.setBuffers(D_volumeData, volumePitch, D_sinoData, sinoPitch);
- if (!ok)
- return false;
-
- ok = sirt.iterate(iterations);
-
- return ok;
-}
-
}
#ifdef STANDALONE
diff --git a/cuda/3d/fdk.cu b/cuda/3d/fdk.cu
index 27357ad..91b0219 100644
--- a/cuda/3d/fdk.cu
+++ b/cuda/3d/fdk.cu
@@ -233,7 +233,7 @@ bool FDK_Filter(cudaPitchedPtr D_projData,
// The filtering is a regular ramp filter per detector line.
int iPaddedDetCount = calcNextPowerOfTwo(2 * dims.iProjU);
- int iHalfFFTSize = calcFFTFourSize(iPaddedDetCount);
+ int iHalfFFTSize = astraCUDA::calcFFTFourierSize(iPaddedDetCount);
int projPitch = D_projData.pitch/sizeof(float);
@@ -241,22 +241,22 @@ bool FDK_Filter(cudaPitchedPtr D_projData,
float* D_sinoData = (float*)D_projData.ptr;
cufftComplex * D_sinoFFT = NULL;
- allocateComplexOnDevice(dims.iProjAngles, iHalfFFTSize, &D_sinoFFT);
+ astraCUDA::allocateComplexOnDevice(dims.iProjAngles, iHalfFFTSize, &D_sinoFFT);
bool ok = true;
for (int v = 0; v < dims.iProjV; ++v) {
- ok = runCudaFFT(dims.iProjAngles, D_sinoData, projPitch,
+ ok = astraCUDA::runCudaFFT(dims.iProjAngles, D_sinoData, projPitch,
dims.iProjU, iPaddedDetCount, iHalfFFTSize,
D_sinoFFT);
if (!ok) break;
- applyFilter(dims.iProjAngles, iHalfFFTSize, D_sinoFFT, D_filter);
+ astraCUDA::applyFilter(dims.iProjAngles, iHalfFFTSize, D_sinoFFT, D_filter);
- ok = runCudaIFFT(dims.iProjAngles, D_sinoFFT, D_sinoData, projPitch,
+ ok = astraCUDA::runCudaIFFT(dims.iProjAngles, D_sinoFFT, D_sinoData, projPitch,
dims.iProjU, iPaddedDetCount, iHalfFFTSize);
if (!ok) break;
@@ -264,7 +264,7 @@ bool FDK_Filter(cudaPitchedPtr D_projData,
D_sinoData += (dims.iProjAngles * projPitch);
}
- freeComplexOnDevice(D_sinoFFT);
+ astraCUDA::freeComplexOnDevice(D_sinoFFT);
return ok;
}
@@ -281,7 +281,7 @@ bool FDK(cudaPitchedPtr D_volumeData,
// TODO: Check errors
cufftComplex * D_filter;
int iPaddedDetCount = calcNextPowerOfTwo(2 * dims.iProjU);
- int iHalfFFTSize = calcFFTFourSize(iPaddedDetCount);
+ int iHalfFFTSize = astraCUDA::calcFFTFourierSize(iPaddedDetCount);
// NB: We don't support arbitrary cone_vec geometries here.
@@ -326,7 +326,7 @@ bool FDK(cudaPitchedPtr D_volumeData,
memset(pHostFilter, 0, sizeof(cufftComplex) * dims.iProjAngles * iHalfFFTSize);
if (pfFilter == 0){
- genFilter(FILTER_RAMLAK, 1.0f, dims.iProjAngles, pHostFilter, iPaddedDetCount, iHalfFFTSize);
+ astraCUDA::genFilter(astra::FILTER_RAMLAK, 1.0f, dims.iProjAngles, pHostFilter, iPaddedDetCount, iHalfFFTSize);
} else {
for (int i = 0; i < dims.iProjAngles * iHalfFFTSize; i++) {
pHostFilter[i].x = pfFilter[i];
@@ -335,8 +335,8 @@ bool FDK(cudaPitchedPtr D_volumeData,
}
- allocateComplexOnDevice(dims.iProjAngles, iHalfFFTSize, &D_filter);
- uploadComplexArrayToDevice(dims.iProjAngles, iHalfFFTSize, pHostFilter, D_filter);
+ astraCUDA::allocateComplexOnDevice(dims.iProjAngles, iHalfFFTSize, &D_filter);
+ astraCUDA::uploadComplexArrayToDevice(dims.iProjAngles, iHalfFFTSize, pHostFilter, D_filter);
delete [] pHostFilter;
@@ -348,7 +348,7 @@ bool FDK(cudaPitchedPtr D_volumeData,
ok = FDK_Filter(D_projData, D_filter, dims);
// Clean up filter
- freeComplexOnDevice(D_filter);
+ astraCUDA::freeComplexOnDevice(D_filter);
#endif
if (!ok)
diff --git a/include/astra/CudaFilteredBackProjectionAlgorithm.h b/include/astra/CudaFilteredBackProjectionAlgorithm.h
index 164b51b..55191ef 100644
--- a/include/astra/CudaFilteredBackProjectionAlgorithm.h
+++ b/include/astra/CudaFilteredBackProjectionAlgorithm.h
@@ -25,30 +25,26 @@ along with the ASTRA Toolbox. If not, see <http://www.gnu.org/licenses/>.
-----------------------------------------------------------------------
*/
-#ifndef CUDAFILTEREDBACKPROJECTIONALGORITHM2_H
-#define CUDAFILTEREDBACKPROJECTIONALGORITHM2_H
+#ifndef CUDAFILTEREDBACKPROJECTIONALGORITHM_H
+#define CUDAFILTEREDBACKPROJECTIONALGORITHM_H
#ifdef ASTRA_CUDA
#include <astra/Float32ProjectionData2D.h>
#include <astra/Float32VolumeData2D.h>
-#include <astra/ReconstructionAlgorithm2D.h>
+#include <astra/CudaReconstructionAlgorithm2D.h>
#include "../../cuda/2d/astra.h"
namespace astra
{
-class _AstraExport CCudaFilteredBackProjectionAlgorithm : public CReconstructionAlgorithm2D
+class _AstraExport CCudaFilteredBackProjectionAlgorithm : public CCudaReconstructionAlgorithm2D
{
public:
static std::string type;
private:
- CFloat32ProjectionData2D * m_pSinogram;
- CFloat32VolumeData2D * m_pReconstruction;
- int m_iGPUIndex;
- int m_iPixelSuperSampling;
E_FBPFILTER m_eFilter;
float * m_pfFilter;
int m_iFilterWidth; // number of elements per projection direction in filter
@@ -65,11 +61,6 @@ public:
virtual bool initialize(const Config& _cfg);
bool initialize(CFloat32ProjectionData2D * _pSinogram, CFloat32VolumeData2D * _pReconstruction, E_FBPFILTER _eFilter, const float * _pfFilter = NULL, int _iFilterWidth = 0, int _iGPUIndex = -1, float _fFilterParameter = -1.0f);
- virtual void run(int _iNrIterations = 0);
-
- static int calcIdealRealFilterWidth(int _iDetectorCount);
- static int calcIdealFourierFilterWidth(int _iDetectorCount);
-
/** Get a description of the class.
*
* @return description string
@@ -79,12 +70,7 @@ public:
protected:
bool check();
- AstraFBP* m_pFBP;
-
- bool m_bAstraFBPInit;
-
- void initializeFromProjector();
- virtual bool requiresProjector() const { return false; }
+ virtual void initCUDAAlgorithm();
};
// inline functions
diff --git a/include/astra/FanFlatBeamLineKernelProjector2D.inl b/include/astra/FanFlatBeamLineKernelProjector2D.inl
index d967844..eb73de8 100644
--- a/include/astra/FanFlatBeamLineKernelProjector2D.inl
+++ b/include/astra/FanFlatBeamLineKernelProjector2D.inl
@@ -25,6 +25,7 @@ along with the ASTRA Toolbox. If not, see <http://www.gnu.org/licenses/>.
-----------------------------------------------------------------------
*/
+#define policy_weight(p,rayindex,volindex,weight) do { if (p.pixelPrior(volindex)) { p.addWeight(rayindex, volindex, weight); p.pixelPosterior(volindex); } } while (false)
template <typename Policy>
void CFanFlatBeamLineKernelProjector2D::project(Policy& p)
@@ -48,246 +49,166 @@ void CFanFlatBeamLineKernelProjector2D::projectSingleRay(int _iProjection, int _
}
//----------------------------------------------------------------------------------------
-// PROJECT BLOCK
+// PROJECT BLOCK - vector projection geometry
template <typename Policy>
void CFanFlatBeamLineKernelProjector2D::projectBlock_internal(int _iProjFrom, int _iProjTo, int _iDetFrom, int _iDetTo, Policy& p)
{
- // variables
- float32 sin_theta, cos_theta, inv_sin_theta, inv_cos_theta, S, T, t, I, P, x, x2;
- float32 lengthPerRow, updatePerRow, inv_pixelLengthX, lengthPerCol, updatePerCol, inv_pixelLengthY;
- int iVolumeIndex, iRayIndex, row, col, iAngle, iDetector, x1;
- bool switch_t;
+ // get vector geometry
+ const CFanFlatVecProjectionGeometry2D* pVecProjectionGeometry;
+ if (dynamic_cast<CFanFlatProjectionGeometry2D*>(m_pProjectionGeometry)) {
+ pVecProjectionGeometry = dynamic_cast<CFanFlatProjectionGeometry2D*>(m_pProjectionGeometry)->toVectorGeometry();
+ } else {
+ pVecProjectionGeometry = dynamic_cast<CFanFlatVecProjectionGeometry2D*>(m_pProjectionGeometry);
+ }
+
+ // precomputations
+ const float32 pixelLengthX = m_pVolumeGeometry->getPixelLengthX();
+ const float32 pixelLengthY = m_pVolumeGeometry->getPixelLengthY();
+ const float32 inv_pixelLengthX = 1.0f / pixelLengthX;
+ const float32 inv_pixelLengthY = 1.0f / pixelLengthY;
+ const int colCount = m_pVolumeGeometry->getGridColCount();
+ const int rowCount = m_pVolumeGeometry->getGridRowCount();
+ const int detCount = pVecProjectionGeometry->getDetectorCount();
+ const float32 Ex = m_pVolumeGeometry->getWindowMinX() + pixelLengthX*0.5f;
+ const float32 Ey = m_pVolumeGeometry->getWindowMaxY() - pixelLengthY*0.5f;
- const CFanFlatProjectionGeometry2D* pProjectionGeometry = dynamic_cast<CFanFlatProjectionGeometry2D*>(m_pProjectionGeometry);
- const CFanFlatVecProjectionGeometry2D* pVecProjectionGeometry = dynamic_cast<CFanFlatVecProjectionGeometry2D*>(m_pProjectionGeometry);
+ // loop angles
+ for (int iAngle = _iProjFrom; iAngle < _iProjTo; ++iAngle) {
- float32 old_theta, theta, alpha;
- const SFanProjection * proj = 0;
+ // variables
+ float32 Dx, Dy, Rx, Ry, S, T, weight, c, r, deltac, deltar, offset, RxOverRy, RyOverRx;
+ float32 lengthPerRow, lengthPerCol, invTminSTimesLengthPerRow, invTminSTimesLengthPerCol;
+ int iVolumeIndex, iRayIndex, row, col, iDetector;
- // loop angles
- for (iAngle = _iProjFrom; iAngle < _iProjTo; ++iAngle) {
-
- // get theta
- if (pProjectionGeometry) {
- old_theta = pProjectionGeometry->getProjectionAngle(iAngle);
- }
- else if (pVecProjectionGeometry) {
- proj = &pVecProjectionGeometry->getProjectionVectors()[iAngle];
- old_theta = atan2(-proj->fSrcX, proj->fSrcY);
- if (old_theta < 0) old_theta += 2*PI;
- } else {
- assert(false);
- }
-
- switch_t = false;
- if (old_theta >= 7*PIdiv4) old_theta -= 2*PI;
- if (old_theta >= 3*PIdiv4) {
- old_theta -= PI;
- switch_t = true;
- }
+ const SFanProjection * proj = &pVecProjectionGeometry->getProjectionVectors()[iAngle];
+
+ float32 detSize = sqrt(proj->fDetUX * proj->fDetUX + proj->fDetUY * proj->fDetUY);
// 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;
+
+ Dx = proj->fDetSX + (iDetector+0.5f) * proj->fDetUX;
+ Dy = proj->fDetSY + (iDetector+0.5f) * proj->fDetUY;
- // 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);
- } else {
- assert(false);
- }
-
- 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();
+ Rx = proj->fSrcX - Dx;
+ Ry = proj->fSrcY - Dy;
- // precalculate S and T
- S = 0.5f - 0.5f * ((updatePerRow < 0) ? -updatePerRow : updatePerRow);
- T = 0.5f - 0.5f * ((updatePerCol < 0) ? -updatePerCol : updatePerCol);
+ bool vertical = fabs(Rx) < fabs(Ry);
+ bool isin = false;
// vertically
- if (old_theta <= PIdiv4) {
-
- // calculate x for row 0
- P = (t - sin_theta * m_pVolumeGeometry->pixelRowToCenterY(0)) * inv_cos_theta;
- x = (P - m_pVolumeGeometry->getWindowMinX()) * inv_pixelLengthX;
+ if (vertical) {
+ RxOverRy = Rx/Ry;
+ lengthPerRow = detSize * pixelLengthX * sqrt(Rx*Rx + Ry*Ry) / abs(Ry);
+ deltac = -pixelLengthY * RxOverRy * inv_pixelLengthX;
+ S = 0.5f - 0.5f*fabs(RxOverRy);
+ T = 0.5f + 0.5f*fabs(RxOverRy);
+ invTminSTimesLengthPerRow = lengthPerRow / (T - S);
+
+ // calculate c for row 0
+ c = (Dx + (Ey - Dy)*RxOverRy - Ex) * inv_pixelLengthX;
// for each row
- for (row = 0; row < m_pVolumeGeometry->getGridRowCount(); ++row) {
-
- // get coords
- x1 = int((x > 0.0f) ? x : x-1.0f);
- x2 = x - x1;
- x += updatePerRow;
+ for (row = 0; row < rowCount; ++row, c += deltac) {
- if (x1 < -1 || x1 > m_pVolumeGeometry->getGridColCount()) continue;
+ col = int(floor(c+0.5f));
+ if (col < -1 || col > colCount) { if (!isin) continue; else break; }
+ offset = c - float32(col);
// left
- if (x2 < 0.5f-S) {
- I = (0.5f - S + x2) / (1.0f - 2.0f*S) * lengthPerRow;
-
- if (x1-1 >= 0 /*&& x1-1 < m_pVolumeGeometry->getGridColCount()*/) {//x1 is always less than or equal to gridColCount because of the "continue" in the beginning of the for-loop
- iVolumeIndex = m_pVolumeGeometry->pixelRowColToIndex(row, x1-1);
- // POLICY: PIXEL PRIOR + ADD + POSTERIOR
- if (p.pixelPrior(iVolumeIndex)) {
- p.addWeight(iRayIndex, iVolumeIndex, lengthPerRow-I);
- p.pixelPosterior(iVolumeIndex);
- }
- }
-
- if (x1 >= 0 && x1 < m_pVolumeGeometry->getGridColCount()) {
- iVolumeIndex = m_pVolumeGeometry->pixelRowColToIndex(row, x1);
- // POLICY: PIXEL PRIOR + ADD + POSTERIOR
- if (p.pixelPrior(iVolumeIndex)) {
- p.addWeight(iRayIndex, iVolumeIndex, I);
- p.pixelPosterior(iVolumeIndex);
- }
- }
- }
+ if (offset < -S) {
+ weight = (offset + T) * invTminSTimesLengthPerRow;
+
+ iVolumeIndex = row * colCount + col - 1;
+ if (col > 0) { policy_weight(p, iRayIndex, iVolumeIndex, lengthPerRow-weight); }
- // center
- else if (x2 <= 0.5f+S) {
- if (x1 >= 0 && x1 < m_pVolumeGeometry->getGridColCount()) {
- iVolumeIndex = m_pVolumeGeometry->pixelRowColToIndex(row, x1);
- // POLICY: PIXEL PRIOR + ADD + POSTERIOR
- if (p.pixelPrior(iVolumeIndex)) {
- p.addWeight(iRayIndex, iVolumeIndex, lengthPerRow);
- p.pixelPosterior(iVolumeIndex);
- }
- }
+ iVolumeIndex++;
+ if (col >= 0 && col < colCount) { policy_weight(p, iRayIndex, iVolumeIndex, weight); }
}
// right
- else if (x2 <= 1.0f) {
- I = (1.5f - S - x2) / (1.0f - 2.0f*S) * lengthPerRow;
-
- if (x1 >= 0 && x1 < m_pVolumeGeometry->getGridColCount()) {
- iVolumeIndex = m_pVolumeGeometry->pixelRowColToIndex(row, x1);
- // POLICY: PIXEL PRIOR + ADD + POSTERIOR
- if (p.pixelPrior(iVolumeIndex)) {
- p.addWeight(iRayIndex, iVolumeIndex, I);
- p.pixelPosterior(iVolumeIndex);
- }
- }
- if (/*x1+1 >= 0 &&*/ x1+1 < m_pVolumeGeometry->getGridColCount()) {//x1 is always greater than or equal to -1 because of the "continue" in the beginning of the for-loop
- iVolumeIndex = m_pVolumeGeometry->pixelRowColToIndex(row, x1+1);
- // POLICY: PIXEL PRIOR + ADD + POSTERIOR
- if (p.pixelPrior(iVolumeIndex)) {
- p.addWeight(iRayIndex, iVolumeIndex, lengthPerRow-I);
- p.pixelPosterior(iVolumeIndex);
- }
- }
+ else if (S < offset) {
+ weight = (offset - S) * invTminSTimesLengthPerRow;
+
+ iVolumeIndex = row * colCount + col;
+ if (col >= 0 && col < colCount) { policy_weight(p, iRayIndex, iVolumeIndex, lengthPerRow-weight); }
+
+ iVolumeIndex++;
+ if (col + 1 < colCount) { policy_weight(p, iRayIndex, iVolumeIndex, weight); }
+ }
+
+ // centre
+ else if (col >= 0 && col < colCount) {
+ iVolumeIndex = row * colCount + col;
+ policy_weight(p, iRayIndex, iVolumeIndex, lengthPerRow);
}
+ isin = true;
}
}
// horizontally
- //else if (PIdiv4 <= old_theta && old_theta <= 3*PIdiv4) {
else {
+ RyOverRx = Ry/Rx;
+ lengthPerCol = detSize * pixelLengthY * sqrt(Rx*Rx + Ry*Ry) / abs(Rx);
+ deltar = -pixelLengthX * RyOverRx * inv_pixelLengthY;
+ S = 0.5f - 0.5f*fabs(RyOverRx);
+ T = 0.5f + 0.5f*fabs(RyOverRx);
+ invTminSTimesLengthPerCol = lengthPerCol / (T - S);
- // calculate point P
- P = (t - cos_theta * m_pVolumeGeometry->pixelColToCenterX(0)) * inv_sin_theta;
- x = (m_pVolumeGeometry->getWindowMaxY() - P) * inv_pixelLengthY;
+ // calculate r for col 0
+ r = -(Dy + (Ex - Dx)*RyOverRx - Ey) * inv_pixelLengthY;
// for each col
- for (col = 0; col < m_pVolumeGeometry->getGridColCount(); ++col) {
-
- // get coords
- x1 = int((x > 0.0f) ? x : x-1.0f);
- x2 = x - x1;
- x += updatePerCol;
+ for (col = 0; col < colCount; ++col, r += deltar) {
- if (x1 < -1 || x1 > m_pVolumeGeometry->getGridRowCount()) continue;
+ row = int(floor(r+0.5f));
+ if (row < -1 || row > rowCount) { if (!isin) continue; else break; }
+ offset = r - float32(row);
// up
- if (x2 < 0.5f-T) {
- I = (0.5f - T + x2) / (1.0f - 2.0f*T) * lengthPerCol;
-
- if (x1-1 >= 0 /*&& x1-1 < m_pVolumeGeometry->getGridRowCount()*/) {//x1 is always less than or equal to gridRowCount because of the "continue" in the beginning of the for-loop
- iVolumeIndex = m_pVolumeGeometry->pixelRowColToIndex(x1-1, col);
- // POLICY: PIXEL PRIOR + ADD + POSTERIOR
- if (p.pixelPrior(iVolumeIndex)) {
- p.addWeight(iRayIndex, iVolumeIndex, lengthPerCol-I);
- p.pixelPosterior(iVolumeIndex);
- }
- }
-
- if (x1 >= 0 && x1 < m_pVolumeGeometry->getGridRowCount()) {
- iVolumeIndex = m_pVolumeGeometry->pixelRowColToIndex(x1, col);
- // POLICY: PIXEL PRIOR + ADD + POSTERIOR
- if (p.pixelPrior(iVolumeIndex)) {
- p.addWeight(iRayIndex, iVolumeIndex, I);
- p.pixelPosterior(iVolumeIndex);
- }
- }
- }
+ if (offset < -S) {
+ weight = (offset + T) * invTminSTimesLengthPerCol;
+
+ iVolumeIndex = (row-1) * colCount + col;
+ if (row > 0) { policy_weight(p, iRayIndex, iVolumeIndex, lengthPerCol-weight); }
- // center
- else if (x2 <= 0.5f+T) {
- if (x1 >= 0 && x1 < m_pVolumeGeometry->getGridRowCount()) {
- iVolumeIndex = m_pVolumeGeometry->pixelRowColToIndex(x1, col);
- // POLICY: PIXEL PRIOR + ADD + POSTERIOR
- if (p.pixelPrior(iVolumeIndex)) {
- p.addWeight(iRayIndex, iVolumeIndex, lengthPerCol);
- p.pixelPosterior(iVolumeIndex);
- }
- }
+ iVolumeIndex += colCount;
+ if (row >= 0 && row < rowCount) { policy_weight(p, iRayIndex, iVolumeIndex, weight); }
}
// down
- else if (x2 <= 1.0f) {
- I = (1.5f - T - x2) / (1.0f - 2.0f*T) * lengthPerCol;
-
- if (x1 >= 0 && x1 < m_pVolumeGeometry->getGridRowCount()) {
- iVolumeIndex = m_pVolumeGeometry->pixelRowColToIndex(x1, col);
- // POLICY: PIXEL PRIOR + ADD + POSTERIOR
- if (p.pixelPrior(iVolumeIndex)) {
- p.addWeight(iRayIndex, iVolumeIndex, I);
- p.pixelPosterior(iVolumeIndex);
- }
- }
- if (/*x1+1 >= 0 &&*/ x1+1 < m_pVolumeGeometry->getGridRowCount()) {//x1 is always greater than or equal to -1 because of the "continue" in the beginning of the for-loop
- iVolumeIndex = m_pVolumeGeometry->pixelRowColToIndex(x1+1, col);
- // POLICY: PIXEL PRIOR + ADD + POSTERIOR
- if (p.pixelPrior(iVolumeIndex)) {
- p.addWeight(iRayIndex, iVolumeIndex, lengthPerCol-I);
- p.pixelPosterior(iVolumeIndex);
- }
- }
+ else if (S < offset) {
+ weight = (offset - S) * invTminSTimesLengthPerCol;
+
+ iVolumeIndex = row * colCount + col;
+ if (row >= 0 && row < rowCount) { policy_weight(p, iRayIndex, iVolumeIndex, lengthPerCol-weight); }
+
+ iVolumeIndex += colCount;
+ if (row + 1 < rowCount) { policy_weight(p, iRayIndex, iVolumeIndex, weight); }
+ }
+
+ // centre
+ else if (row >= 0 && row < rowCount) {
+ iVolumeIndex = row * colCount + col;
+ policy_weight(p, iRayIndex, iVolumeIndex, lengthPerCol);
}
+ isin = true;
}
- } // end loop col
+ }
// POLICY: RAY POSTERIOR
p.rayPosterior(iRayIndex);
} // end loop detector
} // end loop angles
+
+ // Delete created vec geometry if required
+ if (dynamic_cast<CFanFlatProjectionGeometry2D*>(m_pProjectionGeometry))
+ delete pVecProjectionGeometry;
+
}
diff --git a/include/astra/FanFlatProjectionGeometry2D.h b/include/astra/FanFlatProjectionGeometry2D.h
index e9a0535..c45eb78 100644
--- a/include/astra/FanFlatProjectionGeometry2D.h
+++ b/include/astra/FanFlatProjectionGeometry2D.h
@@ -29,6 +29,7 @@ along with the ASTRA Toolbox. If not, see <http://www.gnu.org/licenses/>.
#define _INC_ASTRA_FANFLATPROJECTIONGEOMETRY2D
#include "ProjectionGeometry2D.h"
+#include "FanFlatVecProjectionGeometry2D.h"
#include <cmath>
@@ -189,6 +190,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 6434d3c..914e40d 100644
--- a/include/astra/GeometryUtil2D.h
+++ b/include/astra/GeometryUtil2D.h
@@ -30,17 +30,77 @@ along with the ASTRA Toolbox. If not, see <http://www.gnu.org/licenses/>.
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;
+
+
+ void translate(double dx, double dy) {
+ fDetSX += dx;
+ fDetSY += dy;
+ }
+ void scale(double factor) {
+ fRayX *= factor;
+ fRayY *= factor;
+ fDetSX *= factor;
+ fDetSY *= factor;
+ fDetUX *= factor;
+ fDetUY *= factor;
+ }
+};
+
+
struct SFanProjection {
- // the source
- float fSrcX, fSrcY;
+ // the source
+ float fSrcX, fSrcY;
+
+ // the start of the (linear) detector
+ float fDetSX, fDetSY;
- // the start of the (linear) detector
- float fDetSX, fDetSY;
+ // the length of a single detector pixel
+ float fDetUX, fDetUY;
- // the length of a single detector pixel
- float fDetUX, fDetUY;
+ void translate(double dx, double dy) {
+ fSrcX += dx;
+ fSrcY += dy;
+ fDetSX += dx;
+ fDetSY += dy;
+ }
+ void scale(double factor) {
+ fSrcX *= factor;
+ fSrcY *= factor;
+ fDetSX *= factor;
+ fDetSY *= factor;
+ fDetUX *= factor;
+ fDetUY *= factor;
+ }
};
+
+
+SParProjection* genParProjections(unsigned int iProjAngles,
+ unsigned int iProjDets,
+ double fDetSize,
+ const float *pfAngles,
+ const float *pfExtraOffsets);
+
+SFanProjection* genFanProjections(unsigned int iProjAngles,
+ unsigned int iProjDets,
+ double fOriginSource, double fOriginDetector,
+ double fDetSize,
+ const float *pfAngles);
+
+bool getParParameters(const SParProjection &proj, unsigned int iProjDets, float &fAngle, float &fDetSize, float &fOffset);
+
+bool getFanParameters(const SFanProjection &proj, unsigned int iProjDets, float &fAngle, float &fOriginSource, float &fOriginDetector, float &fDetSize, float &fOffset);
+
+
}
#endif
diff --git a/include/astra/Globals.h b/include/astra/Globals.h
index 128422c..8d0d619 100644
--- a/include/astra/Globals.h
+++ b/include/astra/Globals.h
@@ -127,7 +127,7 @@ namespace astra {
const float32 PI32 = 3.14159265358979323846264338328f;
const float32 PIdiv2 = PI / 2;
const float32 PIdiv4 = PI / 4;
- const float32 eps = 1e-7f;
+ const float32 eps = 1e-6f;
extern _AstraExport bool running_in_matlab;
}
diff --git a/include/astra/ParallelBeamBlobKernelProjector2D.h b/include/astra/ParallelBeamBlobKernelProjector2D.h
index 529cc10..12bee5f 100644
--- a/include/astra/ParallelBeamBlobKernelProjector2D.h
+++ b/include/astra/ParallelBeamBlobKernelProjector2D.h
@@ -214,7 +214,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 c5040b7..ccd2166 100644
--- a/include/astra/ParallelBeamBlobKernelProjector2D.inl
+++ b/include/astra/ParallelBeamBlobKernelProjector2D.inl
@@ -26,186 +26,204 @@ along with the ASTRA Toolbox. If not, see <http://www.gnu.org/licenses/>.
*/
-
-//----------------------------------------------------------------------------------------
-// PROJECT ALL
template <typename Policy>
void CParallelBeamBlobKernelProjector2D::project(Policy& p)
{
- for (int iAngle = 0; iAngle < m_pProjectionGeometry->getProjectionAngleCount(); ++iAngle) {
- for (int iDetector = 0; iDetector < m_pProjectionGeometry->getDetectorCount(); ++iDetector) {
- projectSingleRay(iAngle, iDetector, p);
- }
- }
+ projectBlock_internal(0, m_pProjectionGeometry->getProjectionAngleCount(),
+ 0, m_pProjectionGeometry->getDetectorCount(), p);
}
-
-//----------------------------------------------------------------------------------------
-// PROJECT SINGLE PROJECTION
template <typename Policy>
void CParallelBeamBlobKernelProjector2D::projectSingleProjection(int _iProjection, Policy& p)
{
- for (int iDetector = 0; iDetector < m_pProjectionGeometry->getDetectorCount(); ++iDetector) {
- projectSingleRay(_iProjection, iDetector, p);
- }
+ projectBlock_internal(_iProjection, _iProjection + 1,
+ 0, m_pProjectionGeometry->getDetectorCount(), p);
}
-
-
-//----------------------------------------------------------------------------------------
-// PROJECT SINGLE RAY
template <typename Policy>
void CParallelBeamBlobKernelProjector2D::projectSingleRay(int _iProjection, int _iDetector, Policy& p)
{
- ASTRA_ASSERT(m_bIsInitialized);
+ projectBlock_internal(_iProjection, _iProjection + 1,
+ _iDetector, _iDetector + 1, p);
+}
- int iRayIndex = _iProjection * m_pProjectionGeometry->getDetectorCount() + _iDetector;
+//----------------------------------------------------------------------------------------
+// PROJECT BLOCK - vector projection geometry
+//
+// Kernel limitations: isotropic pixels (PixelLengthX == PixelLengthY)
+//
+// For each angle/detector pair:
+//
+// Let D=(Dx,Dy) denote the centre of the detector (point) in volume coordinates, and
+// let R=(Rx,Ry) denote the direction of the ray (vector).
+//
+// For mainly vertical rays (|Rx|<=|Ry|),
+// let E=(Ex,Ey) denote the centre of the most upper left pixel:
+// E = (WindowMinX + PixelLengthX/2, WindowMaxY - PixelLengthY/2),
+// and let F=(Fx,Fy) denote a vector to the next pixel
+// F = (PixelLengthX, 0)
+//
+// The intersection of the ray (D+aR) with the centre line of the upper row of pixels (E+bF) is
+// { Dx + a*Rx = Ex + b*Fx
+// { Dy + a*Ry = Ey + b*Fy
+// Solving for (a,b) results in:
+// a = (Ey + b*Fy - Dy)/Ry
+// = (Ey - Dy)/Ry
+// b = (Dx + a*Rx - Ex)/Fx
+// = (Dx + (Ey - Dy)*Rx/Ry - Ex)/Fx
+//
+// Define c as the x-value of the intersection of the ray with the upper row in pixel coordinates.
+// c = b
+//
+// The intersection of the ray (D+aR) with the centre line of the second row of pixels (E'+bF) with
+// E'=(WindowMinX + PixelLengthX/2, WindowMaxY - 3*PixelLengthY/2)
+// expressed in x-value pixel coordinates is
+// c' = (Dx + (Ey' - Dy)*Rx/Ry - Ex)/Fx.
+// And thus:
+// deltac = c' - c = (Dx + (Ey' - Dy)*Rx/Ry - Ex)/Fx - (Dx + (Ey - Dy)*Rx/Ry - Ex)/Fx
+// = [(Ey' - Dy)*Rx/Ry - (Ey - Dy)*Rx/Ry]/Fx
+// = [Ey' - Ey]*(Rx/Ry)/Fx
+// = [Ey' - Ey]*(Rx/Ry)/Fx
+// = -PixelLengthY*(Rx/Ry)/Fx.
+//
+// Given c on a certain row, its pixel directly on its left (col), and the distance (offset) to it, can be found:
+// col = floor(c)
+// offset = c - col
+//
+// The index of this pixel is
+// volumeIndex = row * colCount + col
+//
+//
+// Mainly horizontal rays (|Rx|<=|Ry|) are handled in a similar fashion:
+//
+// E = (WindowMinX + PixelLengthX/2, WindowMaxY - PixelLengthY/2),
+// F = (0, -PixelLengthX)
+//
+// a = (Ex + b*Fx - Dx)/Rx = (Ex - Dx)/Rx
+// b = (Dy + a*Ry - Ey)/Fy = (Dy + (Ex - Dx)*Ry/Rx - Ey)/Fy
+// r = b
+// deltar = PixelLengthX*(Ry/Rx)/Fy.
+// row = floor(r+1/2)
+// offset = r - row
+//
+template <typename Policy>
+void CParallelBeamBlobKernelProjector2D::projectBlock_internal(int _iProjFrom, int _iProjTo, int _iDetFrom, int _iDetTo, Policy& p)
+{
+ // get vector geometry
+ const CParallelVecProjectionGeometry2D* pVecProjectionGeometry;
+ if (dynamic_cast<CParallelProjectionGeometry2D*>(m_pProjectionGeometry)) {
+ pVecProjectionGeometry = dynamic_cast<CParallelProjectionGeometry2D*>(m_pProjectionGeometry)->toVectorGeometry();
+ } else {
+ pVecProjectionGeometry = dynamic_cast<CParallelVecProjectionGeometry2D*>(m_pProjectionGeometry);
+ }
- // POLICY: RAY PRIOR
- if (!p.rayPrior(iRayIndex)) return;
+ // precomputations
+ const float32 pixelLengthX = m_pVolumeGeometry->getPixelLengthX();
+ const float32 pixelLengthY = m_pVolumeGeometry->getPixelLengthY();
+ const float32 inv_pixelLengthX = 1.0f / m_pVolumeGeometry->getPixelLengthX();
+ const float32 inv_pixelLengthY = 1.0f / m_pVolumeGeometry->getPixelLengthY();
+ const int colCount = m_pVolumeGeometry->getGridColCount();
+ const int rowCount = m_pVolumeGeometry->getGridRowCount();
+
+ // loop angles
+ for (int iAngle = _iProjFrom; iAngle < _iProjTo; ++iAngle) {
+
+ // variables
+ float32 Dx, Dy, Ex, Ey, c, r, deltac, deltar, offset, invBlobExtent, RxOverRy, RyOverRx;
+ int iVolumeIndex, iRayIndex, row, col, iDetector;
+ int col_left, col_right, row_top, row_bottom, index;
+
+ const SParProjection * proj = &pVecProjectionGeometry->getProjectionVectors()[iAngle];
+
+ bool vertical = fabs(proj->fRayX) < fabs(proj->fRayY);
+ if (vertical) {
+ RxOverRy = proj->fRayX/proj->fRayY;
+ deltac = -m_pVolumeGeometry->getPixelLengthY() * (proj->fRayX/proj->fRayY) * inv_pixelLengthX;
+ invBlobExtent = m_pVolumeGeometry->getPixelLengthY() / abs(m_fBlobSize * sqrt(proj->fRayY*proj->fRayY + proj->fRayX*proj->fRayX) / proj->fRayY);
+ } else {
+ RyOverRx = proj->fRayY/proj->fRayX;
+ deltar = -m_pVolumeGeometry->getPixelLengthX() * (proj->fRayY/proj->fRayX) * inv_pixelLengthY;
+ invBlobExtent = m_pVolumeGeometry->getPixelLengthX() / abs(m_fBlobSize * sqrt(proj->fRayY*proj->fRayY + proj->fRayX*proj->fRayX) / proj->fRayX);
+ }
- // get values
- float32 t = m_pProjectionGeometry->indexToDetectorOffset(_iDetector);
- float32 theta = m_pProjectionGeometry->getProjectionAngle(_iProjection);
- if (theta >= 7*PIdiv4) theta -= 2*PI;
+ Ex = m_pVolumeGeometry->getWindowMinX() + pixelLengthX*0.5f;
+ Ey = m_pVolumeGeometry->getWindowMaxY() - pixelLengthY*0.5f;
- bool flip = false;
+ // loop detectors
+ for (iDetector = _iDetFrom; iDetector < _iDetTo; ++iDetector) {
+
+ iRayIndex = iAngle * m_pProjectionGeometry->getDetectorCount() + iDetector;
- if (theta >= 3*PIdiv4) {
- theta -= PI;
- t = -t;
- flip = true;
- }
+ // POLICY: RAY PRIOR
+ if (!p.rayPrior(iRayIndex)) continue;
+
+ Dx = proj->fDetSX + (iDetector+0.5f) * proj->fDetUX;
+ Dy = proj->fDetSY + (iDetector+0.5f) * proj->fDetUY;
+ // vertically
+ if (vertical) {
- if (theta <= PIdiv4) { // -pi/4 <= theta <= pi/4
+ // calculate c for row 0
+ c = (Dx + (Ey - Dy)*RxOverRy - Ex) * inv_pixelLengthX;
- // precalculate sin, cos, 1/cos
- float32 sin_theta = sin(theta);
- float32 cos_theta = cos(theta);
- float32 inv_cos_theta = 1.0f / cos_theta;
+ // loop rows
+ for (row = 0; row < rowCount; ++row, c += deltac) {
- // precalculate other stuff
- float32 lengthPerRow = m_pVolumeGeometry->getPixelLengthY() * inv_cos_theta;
- float32 updatePerRow = sin_theta * lengthPerRow;
- float32 inv_pixelLengthX = 1.0f / m_pVolumeGeometry->getPixelLengthX();
- float32 pixelLengthX_over_blobSize = m_pVolumeGeometry->getPixelLengthX() / m_fBlobSize;
-
- // some variables
- int row, col, xmin, xmax;
- float32 P, x, d;
+ col_left = int(c - 0.5f - m_fBlobSize);
+ col_right = int(c + 0.5f + m_fBlobSize);
- // calculate P and x for row 0
- P = (t - sin_theta * m_pVolumeGeometry->pixelRowToCenterY(0)) * inv_cos_theta;
- x = (P - m_pVolumeGeometry->getWindowMinX()) * inv_pixelLengthX - 0.5f;
+ if (col_left < 0) col_left = 0;
+ if (col_right > colCount-1) col_right = colCount-1;
- // for each row
- for (row = 0; row < m_pVolumeGeometry->getGridRowCount(); ++row) {
-
- // calculate extent
- xmin = (int)ceil((P - m_fBlobSize - m_pVolumeGeometry->getWindowMinX()) * inv_pixelLengthX - 0.5f);
- xmax = (int)floor((P + m_fBlobSize - m_pVolumeGeometry->getWindowMinX()) * inv_pixelLengthX - 0.5f);
-
- // add pixels
- for (col = xmin; col <= xmax; col++) {
- if (col >= 0 && col < m_pVolumeGeometry->getGridColCount()) {
- //d = abs(x - col) * pixelLengthX_over_blobSize;
- //index = (int)(d*m_iBlobSampleCount+0.5f);
- //float32 fWeight = m_pfBlobValues[min(index,m_iBlobSampleCount-1)] * lengthPerRow;
-
- float32 fWeight;
- int index;
- if ((x >= col) ^ flip) {
- d = abs(x - col) * pixelLengthX_over_blobSize * cos_theta;
- index = (int)(d*m_iBlobSampleCount+0.5f);
- fWeight = m_pfBlobValues[min(index,m_iBlobSampleCount-1)];
- } else {
- d = abs(x - col) * pixelLengthX_over_blobSize * cos_theta;
- index = (int)(d*m_iBlobSampleCount+0.5f);
- fWeight = m_pfBlobValuesNeg[min(index,m_iBlobSampleCount-1)];
- }
+ // loop columns
+ for (col = col_left; col <= col_right; ++col) {
- int iVolumeIndex = m_pVolumeGeometry->pixelRowColToIndex(row, col);
- // POLICY: PIXEL PRIOR + ADD + POSTERIOR
- if (p.pixelPrior(iVolumeIndex)) {
- p.addWeight(iRayIndex, iVolumeIndex, fWeight);
- p.pixelPosterior(iVolumeIndex);
+ iVolumeIndex = row * colCount + col;
+ // POLICY: PIXEL PRIOR + ADD + POSTERIOR
+ if (p.pixelPrior(iVolumeIndex)) {
+ offset = abs(c - float32(col)) * invBlobExtent;
+ index = (int)(offset*m_iBlobSampleCount+0.5f);
+ p.addWeight(iRayIndex, iVolumeIndex, m_pfBlobValues[min(index,m_iBlobSampleCount-1)]);
+ p.pixelPosterior(iVolumeIndex);
+ }
}
}
}
- // update P and x
- P += updatePerRow;
- x += updatePerRow * inv_pixelLengthX;
- }
+ // horizontally
+ else {
- } else { // pi/4 < theta < 3pi/4
-
- // precalculate sin cos
- float32 sin_90_theta = sin(PIdiv2-theta);
- float32 cos_90_theta = cos(PIdiv2-theta);
- float32 inv_cos_90_theta = 1.0f / cos_90_theta;
-
- // precalculate other stuff
- float32 lengthPerCol = m_pVolumeGeometry->getPixelLengthX() * inv_cos_90_theta;
- float32 updatePerCol = sin_90_theta * lengthPerCol;
- float32 inv_pixelLengthY = 1.0f / m_pVolumeGeometry->getPixelLengthY();
- float32 pixelLengthY_over_blobSize = m_pVolumeGeometry->getPixelLengthY() / m_fBlobSize;
-
- // some variables
- int row, col, xmin, xmax;
- float32 P,x, d;
-
- // calculate P and x for col 0
- P = (sin_90_theta * m_pVolumeGeometry->pixelColToCenterX(0) - t) * inv_cos_90_theta;
- x = (P - m_pVolumeGeometry->getWindowMinY()) * inv_pixelLengthY - 0.5f;
-
- // for each col
- for (col = 0; col < m_pVolumeGeometry->getGridColCount(); ++col) {
-
- // calculate extent
- xmin = (int)ceil((P - m_fBlobSize - m_pVolumeGeometry->getWindowMinY()) * inv_pixelLengthY - 0.5f);
- xmax = (int)floor((P + m_fBlobSize - m_pVolumeGeometry->getWindowMinY()) * inv_pixelLengthY - 0.5f);
-
- // add pixels
- for (row = xmin; row <= xmax; row++) {
- if (row >= 0 && row < m_pVolumeGeometry->getGridRowCount()) {
- //d = abs(x - row) * pixelLengthY_over_blobSize;
- //int index = (int)(d*m_iBlobSampleCount+0.5f);
- //float32 fWeight = m_pfBlobValues[min(index,m_iBlobSampleCount-1)] * lengthPerCol;
-
- float32 fWeight;
- int index;
- if ((x <= row) ^ flip) {
- d = abs(x - row) * pixelLengthY_over_blobSize * cos_90_theta;
- index = (int)(d*m_iBlobSampleCount+0.5f);
- fWeight = m_pfBlobValues[min(index,m_iBlobSampleCount-1)];
- } else {
- d = abs(x - row) * pixelLengthY_over_blobSize * cos_90_theta;
- index = (int)(d*m_iBlobSampleCount+0.5f);
- fWeight = m_pfBlobValuesNeg[min(index,m_iBlobSampleCount-1)];
- }
-
-
- int iVolumeIndex = m_pVolumeGeometry->pixelRowColToIndex(row, col);
- // POLICY: PIXEL PRIOR + ADD + POSTERIOR
- if (p.pixelPrior(iVolumeIndex)) {
- p.addWeight(iRayIndex, iVolumeIndex, fWeight);
- p.pixelPosterior(iVolumeIndex);
- }
- }
- }
+ // calculate r for col 0
+ r = -(Dy + (Ex - Dx)*RyOverRx - Ey) * inv_pixelLengthY;
- // update P and x
- P += updatePerCol;
- x += updatePerCol * inv_pixelLengthY;
- }
+ // loop columns
+ for (col = 0; col < colCount; ++col, r += deltar) {
- }
+ row_top = int(r - 0.5f - m_fBlobSize);
+ row_bottom = int(r + 0.5f + m_fBlobSize);
- // POLICY: RAY POSTERIOR
- p.rayPosterior(iRayIndex);
+ if (row_top < 0) row_top = 0;
+ if (row_bottom > rowCount-1) row_bottom = rowCount-1;
+ // loop rows
+ for (row = row_top; row <= row_bottom; ++row) {
+ iVolumeIndex = row * colCount + col;
+ // POLICY: PIXEL PRIOR + ADD + POSTERIOR
+ if (p.pixelPrior(iVolumeIndex)) {
+ offset = abs(r - float32(row)) * invBlobExtent;
+ index = (int)(offset*m_iBlobSampleCount+0.5f);
+ p.addWeight(iRayIndex, iVolumeIndex, m_pfBlobValues[min(index,m_iBlobSampleCount-1)]);
+ p.pixelPosterior(iVolumeIndex);
+ }
+ }
+ }
+ }
+
+ // POLICY: RAY POSTERIOR
+ p.rayPosterior(iRayIndex);
+
+ } // end loop detector
+ } // end loop angles
}
diff --git a/include/astra/ParallelBeamLineKernelProjector2D.h b/include/astra/ParallelBeamLineKernelProjector2D.h
index 0f25d83..e0b7b46 100644
--- a/include/astra/ParallelBeamLineKernelProjector2D.h
+++ b/include/astra/ParallelBeamLineKernelProjector2D.h
@@ -29,6 +29,7 @@ along with the ASTRA Toolbox. If not, see <http://www.gnu.org/licenses/>.
#define _INC_ASTRA_PARALLELBEAMLINEKERNELPROJECTOR
#include "ParallelProjectionGeometry2D.h"
+#include "ParallelVecProjectionGeometry2D.h"
#include "Float32Data2D.h"
#include "Projector2D.h"
@@ -179,6 +180,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 dcd11bd..d07f989 100644
--- a/include/astra/ParallelBeamLineKernelProjector2D.inl
+++ b/include/astra/ParallelBeamLineKernelProjector2D.inl
@@ -24,12 +24,13 @@ along with the ASTRA Toolbox. If not, see <http://www.gnu.org/licenses/>.
-----------------------------------------------------------------------
*/
+#define policy_weight(p,rayindex,volindex,weight) do { if (p.pixelPrior(volindex)) { p.addWeight(rayindex, volindex, weight); p.pixelPosterior(volindex); } } while (false)
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,235 +49,245 @@ void CParallelBeamLineKernelProjector2D::projectSingleRay(int _iProjection, int
//----------------------------------------------------------------------------------------
-// PROJECT BLOCK
+/* PROJECT BLOCK - vector projection geometry
+
+ Kernel limitations: isotropic pixels (PixelLengthX == PixelLengthY)
+
+ For each angle/detector pair:
+
+ Let D=(Dx,Dy) denote the centre of the detector (point) in volume coordinates, and
+ let R=(Rx,Ry) denote the direction of the ray (vector).
+
+ For mainly vertical rays (|Rx|<=|Ry|),
+ let E=(Ex,Ey) denote the centre of the most upper left pixel:
+ E = (WindowMinX + PixelLengthX/2, WindowMaxY - PixelLengthY/2),
+ and let F=(Fx,Fy) denote a vector to the next pixel
+ F = (PixelLengthX, 0)
+
+ The intersection of the ray (D+aR) with the centre line of the upper row of pixels (E+bF) is
+ { Dx + a*Rx = Ex + b*Fx
+ { Dy + a*Ry = Ey + b*Fy
+ Solving for (a,b) results in:
+ a = (Ey + b*Fy - Dy)/Ry
+ = (Ey - Dy)/Ry
+ b = (Dx + a*Rx - Ex)/Fx
+ = (Dx + (Ey - Dy)*Rx/Ry - Ex)/Fx
+
+ Define c as the x-value of the intersection of the ray with the upper row in pixel coordinates.
+ c = b
+
+ The intersection of the ray (D+aR) with the centre line of the second row of pixels (E'+bF) with
+ E'=(WindowMinX + PixelLengthX/2, WindowMaxY - 3*PixelLengthY/2)
+ expressed in x-value pixel coordinates is
+ c' = (Dx + (Ey' - Dy)*Rx/Ry - Ex)/Fx.
+ And thus:
+ deltac = c' - c = (Dx + (Ey' - Dy)*Rx/Ry - Ex)/Fx - (Dx + (Ey - Dy)*Rx/Ry - Ex)/Fx
+ = [(Ey' - Dy)*Rx/Ry - (Ey - Dy)*Rx/Ry]/Fx
+ = [Ey' - Ey]*(Rx/Ry)/Fx
+ = [Ey' - Ey]*(Rx/Ry)/Fx
+ = -PixelLengthY*(Rx/Ry)/Fx.
+
+ Given c on a certain row, its closest pixel (col), and the distance (offset) to it, can be found:
+ col = floor(c+1/2)
+ offset = c - col
+
+ The index of this pixel is
+ volumeIndex = row * colCount + col
+
+ The projection kernel is defined by
+
+ _____ LengthPerRow
+ /| | |\
+ / | | | \
+ __/ | | | \__ 0
+ -T -S 0 S T
+
+ with S = 1/2 - 1/2*|Rx/Ry|, T = 1/2 + 1/2*|Rx/Ry|, and LengthPerRow = pixelLengthX * sqrt(Rx^2+Ry^2) / |Ry|
+
+ And thus
+ { (offset+T)/(T-S) * LengthPerRow if -T <= offset < S
+ W_(rayIndex,volIndex) = { LengthPerRow if -S <= offset <= S
+ { (offset-S)/(T-S) * LengthPerRow if S < offset <= T
+
+ If -T <= offset < S, the weight for the pixel directly to the left is
+ W_(rayIndex,volIndex-1) = LengthPerRow - (offset+T)/(T-S) * LengthPerRow,
+ and if S < offset <= T, the weight for the pixel directly to the right is
+ W_(rayIndex,volIndex+1) = LengthPerRow - (offset-S)/(T-S) * LengthPerRow.
+
+
+ Mainly horizontal rays (|Rx|<=|Ry|) are handled in a similar fashion:
+
+ E = (WindowMinX + PixelLengthX/2, WindowMaxY - PixelLengthY/2),
+ F = (0, -PixelLengthX)
+
+ a = (Ex + b*Fx - Dx)/Rx = (Ex - Dx)/Rx
+ b = (Dy + a*Ry - Ey)/Fy = (Dy + (Ex - Dx)*Ry/Rx - Ey)/Fy
+ r = b
+ deltar = PixelLengthX*(Ry/Rx)/Fy.
+ row = floor(r+1/2)
+ offset = r - row
+ S = 1/2 - 1/2*|Ry/Rx|
+ T = 1/2 + 1/2*|Ry/Rx|
+ LengthPerCol = pixelLengthY * sqrt(Rx^2+Ry^2) / |Rx|
+
+ { (offset+T)/(T-S) * LengthPerCol if -T <= offset < S
+ W_(rayIndex,volIndex) = { LengthPerCol if -S <= offset <= S
+ { (offset-S)/(T-S) * LengthPerCol if S < offset <= T
+
+ W_(rayIndex,volIndex-colcount) = LengthPerCol - (offset+T)/(T-S) * LengthPerCol
+ W_(rayIndex,volIndex+colcount) = LengthPerCol - (offset-S)/(T-S) * LengthPerCol
+*/
template <typename Policy>
void CParallelBeamLineKernelProjector2D::projectBlock_internal(int _iProjFrom, int _iProjTo, int _iDetFrom, int _iDetTo, Policy& p)
{
- // variables
- float32 theta, sin_theta, cos_theta, inv_sin_theta, inv_cos_theta, S, T, t, I, P, x, x2;
- float32 lengthPerRow, updatePerRow, inv_pixelLengthX, lengthPerCol, updatePerCol, inv_pixelLengthY;
- int iVolumeIndex, iRayIndex, row, col, iAngle, iDetector, x1;
- bool switch_t;
+ // get vector geometry
+ const CParallelVecProjectionGeometry2D* pVecProjectionGeometry;
+ if (dynamic_cast<CParallelProjectionGeometry2D*>(m_pProjectionGeometry)) {
+ pVecProjectionGeometry = dynamic_cast<CParallelProjectionGeometry2D*>(m_pProjectionGeometry)->toVectorGeometry();
+ } else {
+ pVecProjectionGeometry = dynamic_cast<CParallelVecProjectionGeometry2D*>(m_pProjectionGeometry);
+ }
+
+ // precomputations
+ const float32 pixelLengthX = m_pVolumeGeometry->getPixelLengthX();
+ const float32 pixelLengthY = m_pVolumeGeometry->getPixelLengthY();
+ const float32 inv_pixelLengthX = 1.0f / pixelLengthX;
+ const float32 inv_pixelLengthY = 1.0f / pixelLengthY;
+ const int colCount = m_pVolumeGeometry->getGridColCount();
+ const int rowCount = m_pVolumeGeometry->getGridRowCount();
// 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;
+ for (int iAngle = _iProjFrom; iAngle < _iProjTo; ++iAngle) {
+
+ // variables
+ float32 Dx, Dy, Ex, Ey, S, T, weight, c, r, deltac, deltar, offset;
+ float32 RxOverRy, RyOverRx, lengthPerRow, lengthPerCol, invTminSTimesLengthPerRow, invTminSTimesLengthPerCol;
+ int iVolumeIndex, iRayIndex, row, col;
+
+ const SParProjection * proj = &pVecProjectionGeometry->getProjectionVectors()[iAngle];
+
+ float32 detSize = sqrt(proj->fDetUX * proj->fDetUX + proj->fDetUY * proj->fDetUY);
+
+ bool vertical = fabs(proj->fRayX) < fabs(proj->fRayY);
+ if (vertical) {
+ RxOverRy = proj->fRayX/proj->fRayY;
+ lengthPerRow = detSize * pixelLengthX * sqrt(proj->fRayY*proj->fRayY + proj->fRayX*proj->fRayX) / abs(proj->fRayY);
+ deltac = -pixelLengthY * RxOverRy * inv_pixelLengthX;
+ S = 0.5f - 0.5f*fabs(RxOverRy);
+ T = 0.5f + 0.5f*fabs(RxOverRy);
+ invTminSTimesLengthPerRow = lengthPerRow / (T - S);
+ } else {
+ RyOverRx = proj->fRayY/proj->fRayX;
+ lengthPerCol = detSize * pixelLengthY * sqrt(proj->fRayY*proj->fRayY + proj->fRayX*proj->fRayX) / abs(proj->fRayX);
+ deltar = -pixelLengthX * RyOverRx * inv_pixelLengthY;
+ S = 0.5f - 0.5f*fabs(RyOverRx);
+ T = 0.5f + 0.5f*fabs(RyOverRx);
+ invTminSTimesLengthPerCol = lengthPerCol / (T - S);
}
- // precalculate sin, cos, 1/cos
- sin_theta = sin(theta);
- cos_theta = cos(theta);
- inv_sin_theta = 1.0f / sin_theta;
- inv_cos_theta = 1.0f / cos_theta;
-
- // precalculate kernel limits
- lengthPerRow = m_pVolumeGeometry->getPixelLengthY() * inv_cos_theta;
- updatePerRow = sin_theta * inv_cos_theta;
- inv_pixelLengthX = 1.0f / m_pVolumeGeometry->getPixelLengthX();
-
- // precalculate kernel limits
- lengthPerCol = m_pVolumeGeometry->getPixelLengthX() * inv_sin_theta;
- updatePerCol = cos_theta * inv_sin_theta;
- inv_pixelLengthY = 1.0f / m_pVolumeGeometry->getPixelLengthY();
-
- // precalculate S and T
- S = 0.5f - 0.5f * ((updatePerRow < 0) ? -updatePerRow : updatePerRow);
- T = 0.5f - 0.5f * ((updatePerCol < 0) ? -updatePerCol : updatePerCol);
+ Ex = m_pVolumeGeometry->getWindowMinX() + pixelLengthX*0.5f;
+ Ey = m_pVolumeGeometry->getWindowMaxY() - pixelLengthY*0.5f;
// loop detectors
- for (iDetector = _iDetFrom; iDetector < _iDetTo; ++iDetector) {
-
+ for (int iDetector = _iDetFrom; iDetector < _iDetTo; ++iDetector) {
+
iRayIndex = iAngle * m_pProjectionGeometry->getDetectorCount() + iDetector;
// POLICY: RAY PRIOR
if (!p.rayPrior(iRayIndex)) continue;
-
- // get t
- t = m_pProjectionGeometry->indexToDetectorOffset(iDetector);
- if (switch_t) t = -t;
- // vertically
- if (theta <= PIdiv4) {
+ Dx = proj->fDetSX + (iDetector+0.5f) * proj->fDetUX;
+ Dy = proj->fDetSY + (iDetector+0.5f) * proj->fDetUY;
+
+ bool isin = false;
- // calculate x for row 0
- P = (t - sin_theta * m_pVolumeGeometry->pixelRowToCenterY(0)) * inv_cos_theta;
- x = (P - m_pVolumeGeometry->getWindowMinX()) * inv_pixelLengthX;
+ // vertically
+ if (vertical) {
- // get coords
- int nextx1 = int((x > 0.0f) ? x : x-1.0f);
- float nextx2 = x - nextx1;
+ // calculate c for row 0
+ c = (Dx + (Ey - Dy)*RxOverRy - Ex) * inv_pixelLengthX;
- // for each row
- for (row = 0; row < m_pVolumeGeometry->getGridRowCount(); ++row) {
+ // loop rows
+ for (row = 0; row < rowCount; ++row, c += deltac) {
- x1 = nextx1;
- x2 = nextx2;
+ col = int(floor(c+0.5f));
+ if (col < -1 || col > colCount) { if (!isin) continue; else break; }
+ offset = c - float32(col);
- nextx2 += updatePerRow;
- while (nextx2 >= 1.0f) {
- nextx2 -= 1.0f;
- nextx1++;
- }
- while (nextx2 < 0.0f) {
- nextx2 += 1.0f;
- nextx1--;
- }
+ // left
+ if (offset < -S) {
+ weight = (offset + T) * invTminSTimesLengthPerRow;
- if (x1 < -1 || x1 > m_pVolumeGeometry->getGridColCount()) continue;
+ iVolumeIndex = row * colCount + col - 1;
+ if (col > 0) { policy_weight(p, iRayIndex, iVolumeIndex, lengthPerRow-weight); }
- // 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 (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++;
+ if (col >= 0 && col < colCount) { policy_weight(p, iRayIndex, iVolumeIndex, weight); }
}
- // 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 (S < offset) {
+ weight = (offset - S) * invTminSTimesLengthPerRow;
+
+ iVolumeIndex = row * colCount + col;
+ if (col >= 0 && col < colCount) { policy_weight(p, iRayIndex, iVolumeIndex, lengthPerRow-weight); }
+
+ iVolumeIndex++;
+ if (col + 1 < colCount) { policy_weight(p, iRayIndex, iVolumeIndex, weight); }
}
- // 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);
- }
- }
- 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 if (col >= 0 && col < colCount) {
+ iVolumeIndex = row * colCount + col;
+ policy_weight(p, iRayIndex, iVolumeIndex, lengthPerRow);
}
+ isin = true;
}
}
// horizontally
- else if (PIdiv4 <= theta && theta <= 3*PIdiv4) {
+ else {
- // calculate point P
- P = (t - cos_theta * m_pVolumeGeometry->pixelColToCenterX(0)) * inv_sin_theta;
- x = (m_pVolumeGeometry->getWindowMaxY() - P) * inv_pixelLengthY;
+ // calculate r for col 0
+ r = -(Dy + (Ex - Dx)*RyOverRx - Ey) * inv_pixelLengthY;
- // get coords
- int nextx1 = int((x > 0.0f) ? x : x-1.0f);
- float nextx2 = x - nextx1;
+ // loop columns
+ for (col = 0; col < colCount; ++col, r += deltar) {
- // for each col
- for (col = 0; col < m_pVolumeGeometry->getGridColCount(); ++col) {
+ row = int(floor(r+0.5f));
+ if (row < -1 || row > rowCount) { if (!isin) continue; else break; }
+ offset = r - float32(row);
- x1 = nextx1;
- x2 = nextx2;
+ // up
+ if (offset < -S) {
+ weight = (offset + T) * invTminSTimesLengthPerCol;
- nextx2 += updatePerCol;
- while (nextx2 >= 1.0f) {
- nextx2 -= 1.0f;
- nextx1++;
- }
- while (nextx2 < 0.0f) {
- nextx2 += 1.0f;
- nextx1--;
+ iVolumeIndex = (row-1) * colCount + col;
+ if (row > 0) { policy_weight(p, iRayIndex, iVolumeIndex, lengthPerCol-weight); }
+
+ iVolumeIndex += colCount;
+ if (row >= 0 && row < rowCount) { policy_weight(p, iRayIndex, iVolumeIndex, weight); }
}
- if (x1 < -1 || x1 > m_pVolumeGeometry->getGridRowCount()) continue;
+ // down
+ else if (S < offset) {
+ weight = (offset - S) * invTminSTimesLengthPerCol;
- // 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 (x1 >= 0 && x1 < m_pVolumeGeometry->getGridRowCount()) {
- iVolumeIndex = m_pVolumeGeometry->pixelRowColToIndex(x1, col);
- // POLICY: PIXEL PRIOR + ADD + POSTERIOR
- if (p.pixelPrior(iVolumeIndex)) {
- p.addWeight(iRayIndex, iVolumeIndex, I);
- p.pixelPosterior(iVolumeIndex);
- }
- }
- }
+ iVolumeIndex = row * colCount + col;
+ if (row >= 0 && row < rowCount) { policy_weight(p, iRayIndex, iVolumeIndex, lengthPerCol-weight); }
- // center
- else if (x2 <= 0.5f+T) {
- if (x1 >= 0 && x1 < m_pVolumeGeometry->getGridRowCount()) {
- iVolumeIndex = m_pVolumeGeometry->pixelRowColToIndex(x1, col);
- // POLICY: PIXEL PRIOR + ADD + POSTERIOR
- if (p.pixelPrior(iVolumeIndex)) {
- p.addWeight(iRayIndex, iVolumeIndex, lengthPerCol);
- p.pixelPosterior(iVolumeIndex);
- }
- }
+ iVolumeIndex += colCount;
+ if (row + 1 < rowCount) { policy_weight(p, iRayIndex, iVolumeIndex, weight); }
}
- // down
- else {
- I = (1.5f - T - x2) / (1.0f - 2.0f*T) * lengthPerCol;
-
- if (x1 >= 0 && x1 < m_pVolumeGeometry->getGridRowCount()) {
- iVolumeIndex = m_pVolumeGeometry->pixelRowColToIndex(x1, col);
- // POLICY: PIXEL PRIOR + ADD + POSTERIOR
- if (p.pixelPrior(iVolumeIndex)) {
- p.addWeight(iRayIndex, iVolumeIndex, I);
- p.pixelPosterior(iVolumeIndex);
- }
- }
- if (x1+1 >= 0 && x1+1 < m_pVolumeGeometry->getGridRowCount()) {
- iVolumeIndex = m_pVolumeGeometry->pixelRowColToIndex(x1+1, col);
- // POLICY: PIXEL PRIOR + ADD + POSTERIOR
- if (p.pixelPrior(iVolumeIndex)) {
- p.addWeight(iRayIndex, iVolumeIndex, lengthPerCol-I);
- p.pixelPosterior(iVolumeIndex);
- }
- }
+ // centre
+ else if (row >= 0 && row < rowCount) {
+ iVolumeIndex = row * colCount + col;
+ policy_weight(p, iRayIndex, iVolumeIndex, lengthPerCol);
}
+ isin = true;
}
- } // end loop col
+ }
// POLICY: RAY POSTERIOR
p.rayPosterior(iRayIndex);
@@ -284,5 +295,8 @@ void CParallelBeamLineKernelProjector2D::projectBlock_internal(int _iProjFrom, i
} // end loop detector
} // end loop angles
-}
+ // Delete created vec geometry if required
+ if (dynamic_cast<CParallelProjectionGeometry2D*>(m_pProjectionGeometry))
+ delete pVecProjectionGeometry;
+}
diff --git a/include/astra/ParallelBeamLinearKernelProjector2D.h b/include/astra/ParallelBeamLinearKernelProjector2D.h
index 7b40628..3e81fa3 100644
--- a/include/astra/ParallelBeamLinearKernelProjector2D.h
+++ b/include/astra/ParallelBeamLinearKernelProjector2D.h
@@ -29,6 +29,7 @@ along with the ASTRA Toolbox. If not, see <http://www.gnu.org/licenses/>.
#define _INC_ASTRA_PARALLELLINEARKERNELPROJECTOR
#include "ParallelProjectionGeometry2D.h"
+#include "ParallelVecProjectionGeometry2D.h"
#include "Float32Data2D.h"
#include "Projector2D.h"
@@ -184,7 +185,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 5dd4781..61e4973 100644
--- a/include/astra/ParallelBeamLinearKernelProjector2D.inl
+++ b/include/astra/ParallelBeamLinearKernelProjector2D.inl
@@ -25,12 +25,13 @@ along with the ASTRA Toolbox. If not, see <http://www.gnu.org/licenses/>.
-----------------------------------------------------------------------
*/
+#define policy_weight(p,rayindex,volindex,weight) do { if (p.pixelPrior(volindex)) { p.addWeight(rayindex, volindex, weight); p.pixelPosterior(volindex); } } while (false)
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>
@@ -47,45 +48,127 @@ void CParallelBeamLinearKernelProjector2D::projectSingleRay(int _iProjection, in
_iDetector, _iDetector + 1, p);
}
+
+
//----------------------------------------------------------------------------------------
-// PROJECT BLOCK
+/* PROJECT BLOCK - vector projection geometry
+
+ Kernel limitations: isotropic pixels (PixelLengthX == PixelLengthY)
+
+ For each angle/detector pair:
+
+ Let D=(Dx,Dy) denote the centre of the detector (point) in volume coordinates, and
+ let R=(Rx,Ry) denote the direction of the ray (vector).
+
+ For mainly vertical rays (|Rx|<=|Ry|),
+ let E=(Ex,Ey) denote the centre of the most upper left pixel:
+ E = (WindowMinX + PixelLengthX/2, WindowMaxY - PixelLengthY/2),
+ and let F=(Fx,Fy) denote a vector to the next pixel
+ F = (PixelLengthX, 0)
+
+ The intersection of the ray (D+aR) with the centre line of the upper row of pixels (E+bF) is
+ { Dx + a*Rx = Ex + b*Fx
+ { Dy + a*Ry = Ey + b*Fy
+ Solving for (a,b) results in:
+ a = (Ey + b*Fy - Dy)/Ry
+ = (Ey - Dy)/Ry
+ b = (Dx + a*Rx - Ex)/Fx
+ = (Dx + (Ey - Dy)*Rx/Ry - Ex)/Fx
+
+ Define c as the x-value of the intersection of the ray with the upper row in pixel coordinates.
+ c = b
+
+ The intersection of the ray (D+aR) with the centre line of the second row of pixels (E'+bF) with
+ E'=(WindowMinX + PixelLengthX/2, WindowMaxY - 3*PixelLengthY/2)
+ expressed in x-value pixel coordinates is
+ c' = (Dx + (Ey' - Dy)*Rx/Ry - Ex)/Fx.
+ And thus:
+ deltac = c' - c = (Dx + (Ey' - Dy)*Rx/Ry - Ex)/Fx - (Dx + (Ey - Dy)*Rx/Ry - Ex)/Fx
+ = [(Ey' - Dy)*Rx/Ry - (Ey - Dy)*Rx/Ry]/Fx
+ = [Ey' - Ey]*(Rx/Ry)/Fx
+ = [Ey' - Ey]*(Rx/Ry)/Fx
+ = -PixelLengthY*(Rx/Ry)/Fx.
+
+ Given c on a certain row, its pixel directly on its left (col), and the distance (offset) to it, can be found:
+ col = floor(c)
+ offset = c - col
+
+ The index of this pixel is
+ volumeIndex = row * colCount + col
+
+ The projection kernel is defined by
+
+ LengthPerRow
+ /|\
+ / | \
+ __/ | \__ 0
+ p0 p1 p2
+
+ And thus
+ W_(rayIndex,volIndex) = (1 - offset) * lengthPerRow
+ W_(rayIndex,volIndex+1) = offset * lengthPerRow
+
+
+ Mainly horizontal rays (|Rx|<=|Ry|) are handled in a similar fashion:
+
+ E = (WindowMinX + PixelLengthX/2, WindowMaxY - PixelLengthY/2),
+ F = (0, -PixelLengthX)
+
+ a = (Ex + b*Fx - Dx)/Rx = (Ex - Dx)/Rx
+ b = (Dy + a*Ry - Ey)/Fy = (Dy + (Ex - Dx)*Ry/Rx - Ey)/Fy
+ r = b
+ deltar = PixelLengthX*(Ry/Rx)/Fy.
+ row = floor(r+1/2)
+ offset = r - row
+ LengthPerCol = pixelLengthY * sqrt(Rx^2+Ry^2) / |Rx|
+
+ W_(rayIndex,volIndex) = (1 - offset) * lengthPerCol
+ W_(rayIndex,volIndex+colcount) = offset * lengthPerCol
+*/
template <typename Policy>
void CParallelBeamLinearKernelProjector2D::projectBlock_internal(int _iProjFrom, int _iProjTo, int _iDetFrom, int _iDetTo, Policy& p)
{
- // variables
- float32 theta, sin_theta, cos_theta, inv_sin_theta, inv_cos_theta, t;
- float32 lengthPerRow, updatePerRow;
- float32 lengthPerCol, updatePerCol;
- bool switch_t;
- int iAngle, iDetector, iVolumeIndex, iRayIndex;
- int row, col, x1;
- float32 P,x,x2;
+ // get vector geometry
+ const CParallelVecProjectionGeometry2D* pVecProjectionGeometry;
+ if (dynamic_cast<CParallelProjectionGeometry2D*>(m_pProjectionGeometry)) {
+ pVecProjectionGeometry = dynamic_cast<CParallelProjectionGeometry2D*>(m_pProjectionGeometry)->toVectorGeometry();
+ } else {
+ pVecProjectionGeometry = dynamic_cast<CParallelVecProjectionGeometry2D*>(m_pProjectionGeometry);
+ }
+
+ // precomputations
+ const float32 pixelLengthX = m_pVolumeGeometry->getPixelLengthX();
+ const float32 pixelLengthY = m_pVolumeGeometry->getPixelLengthY();
+ const float32 inv_pixelLengthX = 1.0f / pixelLengthX;
+ const float32 inv_pixelLengthY = 1.0f / pixelLengthY;
+ const int colCount = m_pVolumeGeometry->getGridColCount();
+ const int rowCount = m_pVolumeGeometry->getGridRowCount();
// 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;
+ for (int iAngle = _iProjFrom; iAngle < _iProjTo; ++iAngle) {
+
+ // variables
+ float32 Dx, Dy, Ex, Ey, c, r, deltac, deltar, offset;
+ float32 RxOverRy, RyOverRx, lengthPerRow, lengthPerCol;
+ int iVolumeIndex, iRayIndex, row, col, iDetector;
+
+ const SParProjection * proj = &pVecProjectionGeometry->getProjectionVectors()[iAngle];
+
+ float32 detSize = sqrt(proj->fDetUX * proj->fDetUX + proj->fDetUY * proj->fDetUY);
+
+ bool vertical = fabs(proj->fRayX) < fabs(proj->fRayY);
+ if (vertical) {
+ RxOverRy = proj->fRayX/proj->fRayY;
+ lengthPerRow = detSize * m_pVolumeGeometry->getPixelLengthX() * sqrt(proj->fRayY*proj->fRayY + proj->fRayX*proj->fRayX) / abs(proj->fRayY);
+ deltac = -pixelLengthY * RxOverRy * inv_pixelLengthX;
+ } else {
+ RyOverRx = proj->fRayY/proj->fRayX;
+ lengthPerCol = detSize * m_pVolumeGeometry->getPixelLengthY() * sqrt(proj->fRayY*proj->fRayY + proj->fRayX*proj->fRayX) / abs(proj->fRayX);
+ deltar = -pixelLengthX * RyOverRx * inv_pixelLengthY;
}
- // precalculate sin, cos, 1/cos
- sin_theta = sin(theta);
- cos_theta = cos(theta);
- inv_cos_theta = 1.0f / cos_theta;
- inv_sin_theta = 1.0f / sin_theta;
-
- // precalculate kernel limits
- lengthPerRow = m_pVolumeGeometry->getPixelLengthY() * inv_cos_theta;
- updatePerRow = sin_theta * inv_cos_theta;
-
- // precalculate kernel limits
- lengthPerCol = m_pVolumeGeometry->getPixelLengthX() * inv_sin_theta;
- updatePerCol = cos_theta * inv_sin_theta;
+ Ex = m_pVolumeGeometry->getWindowMinX() + pixelLengthX*0.5f;
+ Ey = m_pVolumeGeometry->getWindowMaxY() - pixelLengthY*0.5f;
// loop detectors
for (iDetector = _iDetFrom; iDetector < _iDetTo; ++iDetector) {
@@ -95,79 +178,54 @@ void CParallelBeamLinearKernelProjector2D::projectBlock_internal(int _iProjFrom,
// POLICY: RAY PRIOR
if (!p.rayPrior(iRayIndex)) continue;
- // get t
- t = m_pProjectionGeometry->indexToDetectorOffset(iDetector);
- if (switch_t) {
- t = -t;
- }
+ Dx = proj->fDetSX + (iDetector+0.5f) * proj->fDetUX;
+ Dy = proj->fDetSY + (iDetector+0.5f) * proj->fDetUY;
+
+ bool isin = false;
// vertically
- if (theta <= PIdiv4) {
-
- // calculate x for row 0
- P = (t - sin_theta * m_pVolumeGeometry->pixelRowToCenterY(0)) * inv_cos_theta;
- x = m_pVolumeGeometry->coordXToColF(P) - 0.5f;
+ if (vertical) {
+
+ // calculate c for row 0
+ c = (Dx + (Ey - Dy)*RxOverRy - Ex) * inv_pixelLengthX;
- // for each row
- for (row = 0; row < m_pVolumeGeometry->getGridRowCount(); ++row) {
+ // loop rows
+ for (row = 0; row < rowCount; ++row, c += deltac) {
+
+ col = int(floor(c));
+ if (col < -1 || col >= colCount) { if (!isin) continue; else break; }
+ offset = c - float32(col);
+
+ iVolumeIndex = row * colCount + col;
+ if (col >= 0) { policy_weight(p, iRayIndex, iVolumeIndex, (1.0f - offset) * lengthPerRow); }
- // get coords
- x1 = int((x > 0.0f) ? x : x-1.0f);
- x2 = x - x1;
- x += updatePerRow;
-
- // add weights
- if (x1 >= 0 && x1 < m_pVolumeGeometry->getGridColCount()) {
- iVolumeIndex = m_pVolumeGeometry->pixelRowColToIndex(row, x1);
- // POLICY: PIXEL PRIOR + ADD + POSTERIOR
- if (p.pixelPrior(iVolumeIndex)) {
- p.addWeight(iRayIndex, iVolumeIndex, (1.0f - x2) * lengthPerRow);
- p.pixelPosterior(iVolumeIndex);
- }
- }
- if (x1+1 >= 0 && x1+1 < m_pVolumeGeometry->getGridColCount()) {
- iVolumeIndex = m_pVolumeGeometry->pixelRowColToIndex(row, x1+1);
- // POLICY: PIXEL PRIOR + ADD + POSTERIOR
- if (p.pixelPrior(iVolumeIndex)) {
- p.addWeight(iRayIndex, iVolumeIndex, (x2) * lengthPerRow);
- p.pixelPosterior(iVolumeIndex);
- }
- }
+ iVolumeIndex++;
+ if (col + 1 < colCount) { policy_weight(p, iRayIndex, iVolumeIndex, offset * lengthPerRow); }
+
+ isin = true;
}
}
// horizontally
- else if (PIdiv4 <= theta && theta <= 3*PIdiv4) {
-
- // calculate point P
- P = (t - cos_theta * m_pVolumeGeometry->pixelColToCenterX(0)) * inv_sin_theta;
- x = m_pVolumeGeometry->coordYToRowF(P) - 0.5f;
-
- // for each row
- for (col = 0; col < m_pVolumeGeometry->getGridColCount(); ++col) {
-
- // get coords
- x1 = int((x > 0.0f) ? x : x-1.0f);
- x2 = x - x1;
- x += updatePerCol;
-
- // add weights
- if (x1 >= 0 && x1 < m_pVolumeGeometry->getGridRowCount()) {
- iVolumeIndex = m_pVolumeGeometry->pixelRowColToIndex(x1, col);
- // POLICY: PIXEL PRIOR + ADD + POSTERIOR
- if (p.pixelPrior(iVolumeIndex)) {
- p.addWeight(iRayIndex, iVolumeIndex, (1.0f - x2) * lengthPerCol);
- p.pixelPosterior(iVolumeIndex);
- }
- }
- if (x1+1 >= 0 && x1+1 < m_pVolumeGeometry->getGridRowCount()) {
- iVolumeIndex = m_pVolumeGeometry->pixelRowColToIndex(x1+1, col);
- // POLICY: PIXEL PRIOR + ADD + POSTERIOR
- if (p.pixelPrior(iVolumeIndex)) {
- p.addWeight(iRayIndex, iVolumeIndex, x2 * lengthPerCol);
- p.pixelPosterior(iVolumeIndex);
- }
- }
+ else {
+
+ // calculate r for col 0
+ r = -(Dy + (Ex - Dx)*RyOverRx - Ey) * inv_pixelLengthY;
+
+ // loop columns
+ for (col = 0; col < colCount; ++col, r += deltar) {
+
+ row = int(floor(r));
+ if (row < -1 || row >= rowCount) { if (!isin) continue; else break; }
+ offset = r - float32(row);
+
+ iVolumeIndex = row * colCount + col;
+ if (row >= 0) { policy_weight(p, iRayIndex, iVolumeIndex, (1.0f - offset) * lengthPerCol); }
+
+ iVolumeIndex += colCount;
+ if (row + 1 < rowCount) { policy_weight(p, iRayIndex, iVolumeIndex, offset * lengthPerCol); }
+
+ isin = true;
}
}
@@ -177,5 +235,6 @@ void CParallelBeamLinearKernelProjector2D::projectBlock_internal(int _iProjFrom,
} // end loop detector
} // end loop angles
+ if (dynamic_cast<CParallelProjectionGeometry2D*>(m_pProjectionGeometry))
+ delete pVecProjectionGeometry;
}
-
diff --git a/include/astra/ParallelBeamStripKernelProjector2D.h b/include/astra/ParallelBeamStripKernelProjector2D.h
index 373c7fa..908df1f 100644
--- a/include/astra/ParallelBeamStripKernelProjector2D.h
+++ b/include/astra/ParallelBeamStripKernelProjector2D.h
@@ -29,6 +29,7 @@ along with the ASTRA Toolbox. If not, see <http://www.gnu.org/licenses/>.
#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 e8e3739..fdfcd90 100644
--- a/include/astra/ParallelBeamStripKernelProjector2D.inl
+++ b/include/astra/ParallelBeamStripKernelProjector2D.inl
@@ -29,7 +29,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>
@@ -47,251 +47,244 @@ void CParallelBeamStripKernelProjector2D::projectSingleRay(int _iProjection, int
}
//----------------------------------------------------------------------------------------
-// PROJECT BLOCK
+/* PROJECT BLOCK
+
+ Kernel limitations: isotropic pixels (PixelLengthX == PixelLengthY)
+
+ For each angle/detector pair:
+
+ Let DL=(DLx,DLy) denote the left of the detector (point) in volume coordinates, and
+ Let DR=(DRx,DRy) denote the right of the detector (point) in volume coordinates, and
+ let R=(Rx,Ry) denote the direction of the ray (vector).
+
+ For mainly vertical rays (|Rx|<=|Ry|),
+ let E=(Ex,Ey) denote the centre of the most upper left pixel:
+ E = (WindowMinX + PixelLengthX/2, WindowMaxY - PixelLengthY/2),
+ and let F=(Fx,Fy) denote a vector to the next pixel
+ F = (PixelLengthX, 0)
+
+ The intersection of the left edge of the strip (DL+aR) with the centre line of the upper row of pixels (E+bF) is
+ { DLx + a*Rx = Ex + b*Fx
+ { DLy + a*Ry = Ey + b*Fy
+ Solving for (a,b) results in:
+ a = (Ey + b*Fy - DLy)/Ry
+ = (Ey - DLy)/Ry
+ b = (DLx + a*Rx - Ex)/Fx
+ = (DLx + (Ey - DLy)*Rx/Ry - Ex)/Fx
+
+ Define cL as the x-value of the intersection of the left edge of the strip with the upper row in pixel coordinates.
+ cL = b
+
+ cR, the x-value of the intersection of the right edge of the strip with the upper row in pixel coordinates can be found similarly.
+
+ The intersection of the ray (DL+aR) with the left line of the second row of pixels (E'+bF) with
+ E'=(WindowMinX + PixelLengthX/2, WindowMaxY - 3*PixelLengthY/2)
+ expressed in x-value pixel coordinates is
+ cL' = (DLx + (Ey' - DLy)*Rx/Ry - Ex)/Fx.
+ And thus:
+ deltac = cL' - cL = (DLx + (Ey' - DLy)*Rx/Ry - Ex)/Fx - (DLx + (Ey - DLy)*Rx/Ry - Ex)/Fx
+ = [(Ey' - DLy)*Rx/Ry - (Ey - DLy)*Rx/Ry]/Fx
+ = [Ey' - Ey]*(Rx/Ry)/Fx
+ = [Ey' - Ey]*(Rx/Ry)/Fx
+ = -PixelLengthY*(Rx/Ry)/Fx.
+
+ The projection weight for a certain pixel is defined by the area between two points of
+
+ _____ LengthPerRow
+ /| | |\
+ / | | | \
+ __/ | | | \__ 0
+ -T -S 0 S T
+ with S = 1/2 - 1/2*|Rx/Ry|, T = 1/2 + 1/2*|Rx/Ry|, and LengthPerRow = pixelLengthX * sqrt(Rx^2+Ry^2) / |Ry|
+
+ For a certain row, all columns that are 'hit' by this kernel lie in the interval
+ (col_left, col_right) = (floor(cL-1/2+S), floor(cR+3/2-S))
+
+ The offsets for both is
+ (offsetL, offsetR) = (cL - floor(col_left), cR - floor(col_left))
+
+ The projection weight is found by the difference between the integrated values of the kernel
+ offset <= -T Kernel = 0
+ -T < offset <= -S Kernel = PixelArea/2*(T+offset)^2/(T-S)
+ -S < offset <= S Kernel = PixelArea/2 + offset
+ S < offset <= T Kernel = PixelArea - PixelArea/2*(T-offset)^2/(T-S)
+ T <= offset: Kernel = PixelArea
+*/
template <typename Policy>
void CParallelBeamStripKernelProjector2D::projectBlock_internal(int _iProjFrom, int _iProjTo, int _iDetFrom, int _iDetTo, Policy& p)
{
- ASTRA_ASSERT(m_bIsInitialized);
-
- // Some variables
- float32 theta, t;
- int row, col;
- int iAngle;
- int iDetector;
- float32 res;
- float32 PL, PLimitL, PLimitR;
- float32 xL, xR, XLimitL, XLimitR;
- int x1L,x1R;
- float32 x2L, x2R, updateX;
- int iVolumeIndex, iRayIndex;
-
- float32 sin_theta, cos_theta, inv_sin_theta, inv_cos_theta;
- float32 fabs_sin_theta, fabs_cos_theta, fabs_inv_sin_theta, fabs_inv_cos_theta;
- float32 PW, PH, DW, inv_PW, inv_PH;
- float32 S, T, U, V, inv_4T;
+ // get vector geometry
+ const CParallelVecProjectionGeometry2D* pVecProjectionGeometry;
+ if (dynamic_cast<CParallelProjectionGeometry2D*>(m_pProjectionGeometry)) {
+ pVecProjectionGeometry = dynamic_cast<CParallelProjectionGeometry2D*>(m_pProjectionGeometry)->toVectorGeometry();
+ } else {
+ pVecProjectionGeometry = dynamic_cast<CParallelVecProjectionGeometry2D*>(m_pProjectionGeometry);
+ }
+
+ // precomputations
+ const float32 pixelLengthX = m_pVolumeGeometry->getPixelLengthX();
+ const float32 pixelLengthY = m_pVolumeGeometry->getPixelLengthY();
+ const float32 pixelArea = pixelLengthX * pixelLengthY;
+ const float32 inv_pixelLengthX = 1.0f / pixelLengthX;
+ const float32 inv_pixelLengthY = 1.0f / pixelLengthY;
+ const int colCount = m_pVolumeGeometry->getGridColCount();
+ const int rowCount = m_pVolumeGeometry->getGridRowCount();
+ const int detCount = pVecProjectionGeometry->getDetectorCount();
// loop angles
- for (iAngle = _iProjFrom; iAngle < _iProjTo; ++iAngle) {
-
- // get values
- theta = m_pProjectionGeometry->getProjectionAngle(iAngle);
- bool switch_t = false;
- if (theta >= 7*PIdiv4) theta -= 2*PI;
- if (theta >= 3*PIdiv4) {
- theta -= PI;
- switch_t = true;
- }
-
- // Precalculate sin, cos, 1/cos
- sin_theta = sin(theta);
- cos_theta = cos(theta);
- inv_cos_theta = 1.0f / cos_theta;
- inv_sin_theta = 1.0f / sin_theta;
-
- fabs_sin_theta = (sin_theta < 0.0f) ? -sin_theta : sin_theta;
- fabs_cos_theta = (cos_theta < 0.0f) ? -cos_theta : cos_theta;
- fabs_inv_cos_theta = (inv_cos_theta < 0.0f) ? -inv_cos_theta : inv_cos_theta;
- fabs_inv_sin_theta = (inv_sin_theta < 0.0f) ? -inv_sin_theta : inv_sin_theta;
-
- // Other precalculations
- PW = m_pVolumeGeometry->getPixelLengthX();
- PH = m_pVolumeGeometry->getPixelLengthY();
- DW = m_pProjectionGeometry->getDetectorWidth();
- inv_PW = 1.0f / PW;
- inv_PH = 1.0f / PH;
-
- // [-45?,45?] and [135?,225?]
- if (theta < PIdiv4) {
-
- // Precalculate kernel limits
- S = -0.5f * fabs_sin_theta * fabs_inv_cos_theta;
- T = -S;
- U = 1.0f + S;
- V = 1.0f - S;
- inv_4T = 0.25f / T;
-
- updateX = sin_theta * inv_cos_theta;
-
- // loop detectors
- for (iDetector = _iDetFrom; iDetector < _iDetTo; ++iDetector) {
-
- iRayIndex = iAngle * m_pProjectionGeometry->getDetectorCount() + iDetector;
-
- // POLICY: RAY PRIOR
- if (!p.rayPrior(iRayIndex)) continue;
-
- // get t
- t = m_pProjectionGeometry->indexToDetectorOffset(iDetector);
- if (switch_t) t = -t;
-
- // calculate left strip extremes (volume coordinates)
- PL = (t - sin_theta * m_pVolumeGeometry->pixelRowToCenterY(0) - DW*0.5f) * inv_cos_theta;
- PLimitL = PL - 0.5f * fabs_sin_theta * fabs_inv_cos_theta * PH;
- PLimitR = PLimitL + DW * inv_cos_theta + PH * fabs_sin_theta * fabs_inv_cos_theta;
-
- // calculate strip extremes (pixel coordinates)
- XLimitL = (PLimitL - m_pVolumeGeometry->getWindowMinX()) * inv_PW;
- XLimitR = (PLimitR - m_pVolumeGeometry->getWindowMinX()) * inv_PW;
- xL = (PL - m_pVolumeGeometry->getWindowMinX()) * inv_PW;
- xR = xL + (DW * inv_cos_theta) * inv_PW;
-
- // for each row
- for (row = 0; row < m_pVolumeGeometry->getGridRowCount(); ++row) {
-
- // get strip extremes in column indices
- x1L = int((XLimitL > 0.0f) ? XLimitL : XLimitL-1.0f);
- x1R = int((XLimitR > 0.0f) ? XLimitR : XLimitR-1.0f);
-
- // get coords w.r.t leftmost column hit by strip
- x2L = xL - x1L;
- x2R = xR - x1L;
-
- // update strip extremes for the next row
- XLimitL += updateX;
- XLimitR += updateX;
- xL += updateX;
- xR += updateX;
-
- // for each affected col
- for (col = x1L; col <= x1R; ++col) {
-
- if (col < 0 || col >= m_pVolumeGeometry->getGridColCount()) { x2L -= 1.0f; x2R -= 1.0f; continue; }
-
- iVolumeIndex = m_pVolumeGeometry->pixelRowColToIndex(row, col);
- // POLICY: PIXEL PRIOR
- if (!p.pixelPrior(iVolumeIndex)) { x2L -= 1.0f; x2R -= 1.0f; continue; }
-
- // right
- if (x2R >= V) res = 1.0f;
- else if (x2R > U) res = x2R - (x2R-U)*(x2R-U)*inv_4T;
- else if (x2R >= T) res = x2R;
- else if (x2R > S) res = (x2R-S)*(x2R-S) * inv_4T;
- else { x2L -= 1.0f; x2R -= 1.0f; continue; }
-
- // left
- if (x2L <= S) {} // - 0.0f
- else if (x2L < T) res -= (x2L-S)*(x2L-S) * inv_4T;
- else if (x2L <= U) res -= x2L;
- else if (x2L < V) res -= x2L - (x2L-U)*(x2L-U)*inv_4T;
- else { x2L -= 1.0f; x2R -= 1.0f; continue; }
-
- // POLICY: ADD
- p.addWeight(iRayIndex, iVolumeIndex, PW*PH * res);
-
- // POLICY: PIXEL POSTERIOR
- p.pixelPosterior(iVolumeIndex);
-
- x2L -= 1.0f;
- x2R -= 1.0f;
-
- } // end col loop
-
- } // end row loop
-
- // POLICY: RAY POSTERIOR
- p.rayPosterior(iRayIndex);
-
- } // end detector loop
-
- // [45?,135?] and [225?,315?]
- // horizontaly
+ for (int iAngle = _iProjFrom; iAngle < _iProjTo; ++iAngle) {
+
+ // variables
+ float32 DLx, DLy, DRx, DRy, Ex, Ey, S, T, deltac, deltar, offsetL, offsetR, invTminS;
+ float32 res, RxOverRy, RyOverRx, cL, cR, rL, rR;
+ int iVolumeIndex, iRayIndex, iDetector;
+ int row, row_top, row_bottom, col, col_left, col_right;
+
+ const SParProjection * proj = &pVecProjectionGeometry->getProjectionVectors()[iAngle];
+
+ bool vertical = fabs(proj->fRayX) < fabs(proj->fRayY);
+ if (vertical) {
+ RxOverRy = proj->fRayX/proj->fRayY;
+ deltac = -m_pVolumeGeometry->getPixelLengthY() * RxOverRy * inv_pixelLengthX;
+ S = 0.5f - 0.5f*fabs(RxOverRy);
+ T = 0.5f + 0.5f*fabs(RxOverRy);
+ invTminS = 1.0f / (T-S);
} else {
+ RyOverRx = proj->fRayY/proj->fRayX;
+ deltar = -m_pVolumeGeometry->getPixelLengthX() * RyOverRx * inv_pixelLengthY;
+ S = 0.5f - 0.5f*fabs(RyOverRx);
+ T = 0.5f + 0.5f*fabs(RyOverRx);
+ invTminS = 1.0f / (T-S);
+ }
- // Precalculate kernel limits
- S = -0.5f * fabs_cos_theta * fabs_inv_sin_theta;
- T = -S;
- U = 1.0f + S;
- V = 1.0f - S;
- inv_4T = 0.25f / T;
-
- updateX = cos_theta * inv_sin_theta;
-
- // loop detectors
- for (iDetector = _iDetFrom; iDetector < _iDetTo; ++iDetector) {
-
- iRayIndex = iAngle * m_pProjectionGeometry->getDetectorCount() + iDetector;
-
- // POLICY: RAY PRIOR
- if (!p.rayPrior(iRayIndex)) continue;
+ Ex = m_pVolumeGeometry->getWindowMinX() + pixelLengthX*0.5f;
+ Ey = m_pVolumeGeometry->getWindowMaxY() - pixelLengthY*0.5f;
- // get t
- t = m_pProjectionGeometry->indexToDetectorOffset(iDetector);
- if (switch_t) t = -t;
+ // loop detectors
+ for (iDetector = _iDetFrom; iDetector < _iDetTo; ++iDetector) {
- // calculate left strip extremes (volume coordinates)
- PL = (t - cos_theta * m_pVolumeGeometry->pixelColToCenterX(0) + DW*0.5f) * inv_sin_theta;
- PLimitL = PL + 0.5f * fabs_cos_theta * fabs_inv_sin_theta * PW;
- PLimitR = PLimitL - DW * inv_sin_theta - PH * fabs_cos_theta * fabs_inv_sin_theta;
-
- // calculate strip extremes (pixel coordinates)
- XLimitL = (m_pVolumeGeometry->getWindowMaxY() - PLimitL) * inv_PH;
- XLimitR = (m_pVolumeGeometry->getWindowMaxY() - PLimitR) * inv_PH;
- xL = (m_pVolumeGeometry->getWindowMaxY() - PL) * inv_PH;
- xR = xL + (DW * fabs_inv_sin_theta) * inv_PH;
-
- // for each col
- for (col = 0; col < m_pVolumeGeometry->getGridColCount(); ++col) {
-
- // get strip extremes in column indices
- x1L = int((XLimitL > 0.0f) ? XLimitL : XLimitL-1.0f);
- x1R = int((XLimitR > 0.0f) ? XLimitR : XLimitR-1.0f);
-
- // get coords w.r.t leftmost column hit by strip
- x2L = xL - x1L;
- x2R = xR - x1L;
-
- // update strip extremes for the next row
- XLimitL += updateX;
- XLimitR += updateX;
- xL += updateX;
- xR += updateX;
+ iRayIndex = iAngle * detCount + iDetector;
- // for each affected col
- for (row = x1L; row <= x1R; ++row) {
-
- if (row < 0 || row >= m_pVolumeGeometry->getGridRowCount()) { x2L -= 1.0f; x2R -= 1.0f; continue; }
-
- iVolumeIndex = m_pVolumeGeometry->pixelRowColToIndex(row, col);
-
- // POLICY: PIXEL PRIOR
- if (!p.pixelPrior(iVolumeIndex)) { x2L -= 1.0f; x2R -= 1.0f; continue; }
-
- // right
- if (x2R >= V) res = 1.0f;
- else if (x2R > U) res = x2R - (x2R-U)*(x2R-U)*inv_4T;
- else if (x2R >= T) res = x2R;
- else if (x2R > S) res = (x2R-S)*(x2R-S) * inv_4T;
- else { x2L -= 1.0f; x2R -= 1.0f; continue; }
-
- // left
- if (x2L <= S) {} // - 0.0f
- else if (x2L < T) res -= (x2L-S)*(x2L-S) * inv_4T;
- else if (x2L <= U) res -= x2L;
- else if (x2L < V) res -= x2L - (x2L-U)*(x2L-U)*inv_4T;
- else { x2L -= 1.0f; x2R -= 1.0f; continue; }
-
- // POLICY: ADD
- p.addWeight(iRayIndex, iVolumeIndex, PW*PH * res);
-
- // POLICY: PIXEL POSTERIOR
- p.pixelPosterior(iVolumeIndex);
-
- x2L -= 1.0f;
- x2R -= 1.0f;
-
- } // end row loop
-
- } // end col loop
-
- // POLICY: RAY POSTERIOR
- p.rayPosterior(iRayIndex);
-
- } // end detector loop
-
-
- } // end theta switch
+ // POLICY: RAY PRIOR
+ if (!p.rayPrior(iRayIndex)) continue;
+
+ DLx = proj->fDetSX + iDetector * proj->fDetUX;
+ DLy = proj->fDetSY + iDetector * proj->fDetUY;
+ DRx = DLx + proj->fDetUX;
+ DRy = DLy + proj->fDetUY;
+
+ // vertically
+ if (vertical) {
+
+ // calculate cL and cR for row 0
+ cL = (DLx + (Ey - DLy)*RxOverRy - Ex) * inv_pixelLengthX;
+ cR = (DRx + (Ey - DRy)*RxOverRy - Ex) * inv_pixelLengthX;
+
+ if (cR < cL) {
+ float32 tmp = cL;
+ cL = cR;
+ cR = tmp;
+ }
+
+ // loop rows
+ for (row = 0; row < rowCount; ++row, cL += deltac, cR += deltac) {
+
+ col_left = int(cL-0.5f+S);
+ col_right = int(cR+1.5-S);
+
+ if (col_left < 0) col_left = 0;
+ if (col_right > colCount-1) col_right = colCount-1;
+
+ float32 tmp = float32(col_left);
+ offsetL = cL - tmp;
+ offsetR = cR - tmp;
+
+ // loop columns
+ for (col = col_left; col <= col_right; ++col, offsetL -= 1.0f, offsetR -= 1.0f) {
+
+ iVolumeIndex = row * colCount + col;
+ // POLICY: PIXEL PRIOR + ADD + POSTERIOR
+ if (p.pixelPrior(iVolumeIndex)) {
+
+ // right ray edge
+ if (T <= offsetR) res = 1.0f;
+ else if (S < offsetR) res = 1.0f - 0.5f*(T-offsetR)*(T-offsetR)*invTminS;
+ else if (-S < offsetR) res = 0.5f + offsetR;
+ else if (-T < offsetR) res = 0.5f*(offsetR+T)*(offsetR+T)*invTminS;
+ else res = 0.0f;
+
+ // left ray edge
+ if (T <= offsetL) res -= 1.0f;
+ else if (S < offsetL) res -= 1.0f - 0.5f*(T-offsetL)*(T-offsetL)*invTminS;
+ else if (-S < offsetL) res -= 0.5f + offsetL;
+ else if (-T < offsetL) res -= 0.5f*(offsetL+T)*(offsetL+T)*invTminS;
+
+ p.addWeight(iRayIndex, iVolumeIndex, pixelArea*res);
+ p.pixelPosterior(iVolumeIndex);
+ }
+ }
+ }
+ }
+
+ // horizontally
+ else {
+
+ // calculate rL and rR for row 0
+ rL = -(DLy + (Ex - DLx)*RyOverRx - Ey) * inv_pixelLengthY;
+ rR = -(DRy + (Ex - DRx)*RyOverRx - Ey) * inv_pixelLengthY;
+
+ if (rR < rL) {
+ float32 tmp = rL;
+ rL = rR;
+ rR = tmp;
+ }
+
+ // loop columns
+ for (col = 0; col < colCount; ++col, rL += deltar, rR += deltar) {
+
+ row_top = int(rL-0.5f+S);
+ row_bottom = int(rR+1.5-S);
+
+ if (row_top < 0) row_top = 0;
+ if (row_bottom > rowCount-1) row_bottom = rowCount-1;
+
+ float32 tmp = float32(row_top);
+ offsetL = rL - tmp;
+ offsetR = rR - tmp;
+
+ // loop rows
+ for (row = row_top; row <= row_bottom; ++row, offsetL -= 1.0f, offsetR -= 1.0f) {
+
+ iVolumeIndex = row * colCount + col;
+ // POLICY: PIXEL PRIOR + ADD + POSTERIOR
+ if (p.pixelPrior(iVolumeIndex)) {
+
+ // right ray edge
+ if (T <= offsetR) res = 1.0f;
+ else if (S < offsetR) res = 1.0f - 0.5f*(T-offsetR)*(T-offsetR)*invTminS;
+ else if (-S < offsetR) res = 0.5f + offsetR;
+ else if (-T < offsetR) res = 0.5f*(offsetR+T)*(offsetR+T)*invTminS;
+ else res = 0.0f;
+
+ // left ray edge
+ if (T <= offsetL) res -= 1.0f;
+ else if (S < offsetL) res -= 1.0f - 0.5f*(T-offsetL)*(T-offsetL)*invTminS;
+ else if (-S < offsetL) res -= 0.5f + offsetL;
+ else if (-T < offsetL) res -= 0.5f*(offsetL+T)*(offsetL+T)*invTminS;
+
+ p.addWeight(iRayIndex, iVolumeIndex, pixelArea*res);
+ p.pixelPosterior(iVolumeIndex);
+ }
+ }
+ }
+ }
+
+ // POLICY: RAY POSTERIOR
+ p.rayPosterior(iRayIndex);
+
+ } // end loop detector
+ } // end loop angles
- } // end angle loop
+ if (dynamic_cast<CParallelProjectionGeometry2D*>(m_pProjectionGeometry))
+ delete pVecProjectionGeometry;
}
-
-
diff --git a/include/astra/ParallelProjectionGeometry2D.h b/include/astra/ParallelProjectionGeometry2D.h
index 963bb06..9625d35 100644
--- a/include/astra/ParallelProjectionGeometry2D.h
+++ b/include/astra/ParallelProjectionGeometry2D.h
@@ -29,6 +29,7 @@ along with the ASTRA Toolbox. If not, see <http://www.gnu.org/licenses/>.
#define _INC_ASTRA_PARALLELPROJECTIONGEOMETRY2D
#include "ProjectionGeometry2D.h"
+#include "ParallelVecProjectionGeometry2D.h"
namespace astra
{
@@ -82,8 +83,7 @@ public:
CParallelProjectionGeometry2D(int _iProjectionAngleCount,
int _iDetectorCount,
float32 _fDetectorWidth,
- const float32* _pfProjectionAngles,
- const float32* _pfExtraDetectorOffsets = 0);
+ const float32* _pfProjectionAngles);
/** Copy constructor.
*/
@@ -115,8 +115,7 @@ public:
bool initialize(int _iProjectionAngleCount,
int _iDetectorCount,
float32 _fDetectorWidth,
- const float32* _pfProjectionAngles,
- const float32* _pfExtraDetectorOffsets = 0);
+ const float32* _pfProjectionAngles);
/** Create a hard copy.
*/
@@ -133,7 +132,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.
*
@@ -150,7 +149,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 393db77..504f588 100644
--- a/include/astra/ProjectionGeometry2D.h
+++ b/include/astra/ProjectionGeometry2D.h
@@ -64,10 +64,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.
*/
@@ -93,8 +89,7 @@ protected:
CProjectionGeometry2D(int _iProjectionAngleCount,
int _iDetectorCount,
float32 _fDetectorWidth,
- const float32* _pfProjectionAngles,
- const float32* _pfExtraDetectorOffsets = 0);
+ const float32* _pfProjectionAngles);
/** Copy constructor.
*/
@@ -120,8 +115,7 @@ protected:
bool _initialize(int _iProjectionAngleCount,
int _iDetectorCount,
float32 _fDetectorWidth,
- const float32* _pfProjectionAngles,
- const float32* _pfExtraDetectorOffsets = 0);
+ const float32* _pfProjectionAngles);
public:
@@ -201,9 +195,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
@@ -272,12 +263,6 @@ public:
//----------------------------------------------------------------------------------------
-inline float32 CProjectionGeometry2D::getExtraDetectorOffset(int _iAngle) const
-{
- return m_pfExtraDetectorOffset ? m_pfExtraDetectorOffset[_iAngle] : 0.0f;
-}
-
-
// Get the initialization state.
inline bool CProjectionGeometry2D::isInitialized() const
{
diff --git a/matlab/algorithms/DART/IterativeTomography.m b/matlab/algorithms/DART/IterativeTomography.m
index e94dd36..df414a0 100644
--- a/matlab/algorithms/DART/IterativeTomography.m
+++ b/matlab/algorithms/DART/IterativeTomography.m
@@ -87,7 +87,7 @@ classdef IterativeTomography < matlab.mixin.Copyable
function ok = initialize(this)
% Initialize this object. Returns 1 if succesful.
% >> tomography.initialize();
-
+ disp('sdfqnlmkqdsfmlkjdfqsjklm');
% create projection geometry with super-resolution
if this.superresolution > 1
this.proj_geom_sr = astra_geom_superresolution(this.proj_geom, this.superresolution);
diff --git a/matlab/algorithms/DART/examples/example1.m b/matlab/algorithms/DART/examples/example1.m
index 6c86473..25d5215 100644
--- a/matlab/algorithms/DART/examples/example1.m
+++ b/matlab/algorithms/DART/examples/example1.m
@@ -35,36 +35,41 @@ vol_geom = astra_create_vol_geom(det_count, det_count);
[sinogram_id, sinogram] = astra_create_sino_cuda(I, proj_geom, vol_geom);
astra_mex_data2d('delete', sinogram_id);
-% DART
-D = DARTalgorithm(sinogram, proj_geom);
-D.t0 = 100;
-D.t = 10;
+ % DART
+ D = DARTalgorithm(sinogram, proj_geom);
+ D.t0 = 100;
+ D.t = 10;
-D.tomography.method = 'SIRT_CUDA';
-D.tomography.gpu_core = gpu_core;
-D.tomography.use_minc = 'yes';
+ D.tomography.method = 'SIRT';
+ D.tomography.gpu_core = gpu_core;
+ D.tomography.use_minc = 'yes';
+ D.tomography.gpu = 'no';
-D.segmentation.rho = rho;
-D.segmentation.tau = tau;
+ D.segmentation = SegmentationPDM();
+ D.segmentation.rho = rho*1.8;
+ D.segmentation.tau = tau*1.5;
+ D.segmentation.interval = 5;
-D.smoothing.b = 0.1;
-D.smoothing.gpu_core = gpu_core;
-
-D.masking.random = 0.1;
-D.masking.gpu_core = gpu_core;
+ D.smoothing.b = 0.1;
+ D.smoothing.gpu_core = gpu_core;
+ D.smoothing.gpu = 'no';
+
+ D.masking.random = 0.1;
+ D.masking.gpu_core = gpu_core;
+ D.masking.gpu = 'no';
+
+ D.output.directory = outdir;
+ D.output.pre = [prefix '_'];
+ D.output.save_images = 'no';
+ D.output.save_results = {'stats', 'settings', 'S', 'V'};
+ D.output.save_interval = dart_iterations;
+ D.output.verbose = 'yes';
-D.output.directory = outdir;
-D.output.pre = [prefix '_'];
-D.output.save_images = 'no';
-D.output.save_results = {'stats', 'settings', 'S', 'V'};
-D.output.save_interval = dart_iterations;
-D.output.verbose = 'yes';
+ D.statistics.proj_diff = 'no';
-D.statistics.proj_diff = 'no';
+ D.initialize();
-D.initialize();
-
-D.iterate(dart_iterations);
+ D.iterate(dart_iterations);
% save the reconstruction and the segmentation to file
imwritesc(D.S, [outdir '/' prefix '_S.png']);
diff --git a/matlab/mex/astra_mex_data2d_c.cpp b/matlab/mex/astra_mex_data2d_c.cpp
index ee558e0..3d3a253 100644
--- a/matlab/mex/astra_mex_data2d_c.cpp
+++ b/matlab/mex/astra_mex_data2d_c.cpp
@@ -44,6 +44,7 @@ along with the ASTRA Toolbox. If not, see <http://www.gnu.org/licenses/>.
#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 0a30f23..01c9177 100644
--- a/matlab/tools/astra_create_proj_geom.m
+++ b/matlab/tools/astra_create_proj_geom.m
@@ -106,6 +106,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 ec37d0a..84469aa 100644
--- a/python/astra/PyIncludes.pxd
+++ b/python/astra/PyIncludes.pxd
@@ -128,6 +128,10 @@ cdef extern from "astra/FanFlatVecProjectionGeometry2D.h" namespace "astra":
cdef cppclass CFanFlatVecProjectionGeometry2D:
CFanFlatVecProjectionGeometry2D()
+cdef extern from "astra/ParallelVecProjectionGeometry2D.h" namespace "astra":
+ cdef cppclass CParallelVecProjectionGeometry2D:
+ CParallelVecProjectionGeometry2D()
+
cdef extern from "astra/ParallelProjectionGeometry2D.h" namespace "astra":
cdef cppclass CParallelProjectionGeometry2D:
CParallelProjectionGeometry2D()
diff --git a/python/astra/creators.py b/python/astra/creators.py
index 1e4d937..85daf82 100644
--- a/python/astra/creators.py
+++ b/python/astra/creators.py
@@ -147,6 +147,13 @@ This method can be called in a number of ways:
:type angles: :class:`numpy.ndarray`
:returns: A parallel projection geometry.
+``create_proj_geom('parallel_vec', det_count, V)``:
+
+:param det_count: Number of detector pixels.
+:type det_count: :class:`int`
+:param V: Vector array.
+:type V: :class:`numpy.ndarray`
+:returns: A parallel-beam projection geometry.
``create_proj_geom('fanflat', det_width, det_count, angles, source_origin, origin_det)``:
@@ -234,6 +241,12 @@ This method can be called in a number of ways:
raise Exception(
'not enough variables: astra_create_proj_geom(parallel, detector_spacing, det_count, angles)')
return {'type': 'parallel', 'DetectorWidth': args[0], 'DetectorCount': args[1], 'ProjectionAngles': args[2]}
+ elif intype == 'parallel_vec':
+ if len(args) < 2:
+ raise Exception('not enough variables: astra_create_proj_geom(parallel_vec, det_count, V)')
+ if not args[1].shape[1] == 6:
+ raise Exception('V should be a Nx6 matrix, with N the number of projections')
+ return {'type':'parallel_vec', 'DetectorCount':args[0], 'Vectors':args[1]}
elif intype == 'fanflat':
if len(args) < 5:
raise Exception('not enough variables: astra_create_proj_geom(fanflat, det_width, det_count, angles, source_origin, origin_det)')
diff --git a/python/astra/data2d_c.pyx b/python/astra/data2d_c.pyx
index 9c88073..1b1125d 100644
--- a/python/astra/data2d_c.pyx
+++ b/python/astra/data2d_c.pyx
@@ -111,6 +111,8 @@ def create(datatype, geometry, data=None, link=False):
ppGeometry = <CProjectionGeometry2D * >new CFanFlatProjectionGeometry2D()
elif (tpe == 'fanflat_vec'):
ppGeometry = <CProjectionGeometry2D * >new CFanFlatVecProjectionGeometry2D()
+ elif (tpe == 'parallel_vec'):
+ ppGeometry = <CProjectionGeometry2D * >new CParallelVecProjectionGeometry2D()
else:
ppGeometry = <CProjectionGeometry2D * >new CParallelProjectionGeometry2D()
if not ppGeometry.initialize(cfg[0]):
@@ -225,6 +227,8 @@ def change_geometry(i, geom):
ppGeometry = <CProjectionGeometry2D * >new CFanFlatProjectionGeometry2D()
elif (tpe == 'fanflat_vec'):
ppGeometry = <CProjectionGeometry2D * >new CFanFlatVecProjectionGeometry2D()
+ elif (tpe == 'parallel_vec'):
+ ppGeometry = <CProjectionGeometry2D * >new CParallelVecProjectionGeometry2D()
else:
ppGeometry = <CProjectionGeometry2D * >new CParallelProjectionGeometry2D()
if not ppGeometry.initialize(cfg[0]):
diff --git a/python/astra/functions.py b/python/astra/functions.py
index 7277de5..b3a470d 100644
--- a/python/astra/functions.py
+++ b/python/astra/functions.py
@@ -172,7 +172,27 @@ def geom_2vec(proj_geom):
:param proj_geom: Projection geometry to convert
:type proj_geom: :class:`dict`
"""
- if proj_geom['type'] == 'fanflat':
+
+ if proj_geom['type'] == 'parallel':
+ angles = proj_geom['ProjectionAngles']
+ vectors = np.zeros((len(angles), 6))
+ for i in range(len(angles)):
+
+ # source
+ vectors[i, 0] = np.sin(angles[i])
+ vectors[i, 1] = -np.cos(angles[i])
+
+ # center of detector
+ vectors[i, 2] = 0
+ vectors[i, 3] = 0
+
+ # vector from detector pixel 0 to 1
+ vectors[i, 4] = np.cos(angles[i]) * proj_geom['DetectorWidth']
+ vectors[i, 5] = np.sin(angles[i]) * proj_geom['DetectorWidth']
+ proj_geom_out = ac.create_proj_geom(
+ 'parallel_vec', proj_geom['DetectorCount'], vectors)
+
+ elif proj_geom['type'] == 'fanflat':
angles = proj_geom['ProjectionAngles']
vectors = np.zeros((len(angles), 6))
for i in range(len(angles)):
@@ -251,3 +271,37 @@ def geom_2vec(proj_geom):
raise ValueError(
'No suitable vector geometry found for type: ' + proj_geom['type'])
return proj_geom_out
+
+
+def geom_postalignment(proj_geom, factor):
+ """Returns the size of a volume or sinogram, based on the projection or volume geometry.
+
+ :param proj_geom: input projection geometry (vector-based only, use astra.geom_2vec to convert conventional projection geometries)
+ :type proj_geom: :class:`dict`
+ :param factor: Optional axis index to return
+ :type factor: :class:`float`
+ """
+
+ if proj_geom['type'] == 'parallel_vec' or proj_geom['type'] == 'fanflat_vec':
+ for i in range(proj_geom['Vectors'].shape[0]):
+ proj_geom['Vectors'][i,2] = proj_geom['Vectors'][i,2] + factor * proj_geom['Vectors'][i,4];
+ proj_geom['Vectors'][i,3] = proj_geom['Vectors'][i,3] + factor * proj_geom['Vectors'][i,5];
+
+ elif proj_geom['type'] == 'parallel3d_vec' or proj_geom['type'] == 'cone_vec':
+
+ if len(factor) == 1:
+ for i in range(proj_geom['Vectors'].shape[0]):
+ proj_geom['Vectors'][i,3] = proj_geom['Vectors'][i,3] + factor * proj_geom['Vectors'][i,6];
+ proj_geom['Vectors'][i,4] = proj_geom['Vectors'][i,4] + factor * proj_geom['Vectors'][i,7];
+ proj_geom['Vectors'][i,5] = proj_geom['Vectors'][i,5] + factor * proj_geom['Vectors'][i,8];
+
+ elif len(factor) > 1:
+ for i in range(proj_geom['Vectors'].shape[0]):
+ proj_geom['Vectors'][i,3] = proj_geom['Vectors'][i,3] + factor[0] * proj_geom['Vectors'][i,6] + factor[1] * proj_geom['Vectors'][i, 9];
+ proj_geom['Vectors'][i,4] = proj_geom['Vectors'][i,4] + factor[0] * proj_geom['Vectors'][i,7] + factor[1] * proj_geom['Vectors'][i,10];
+ proj_geom['Vectors'][i,5] = proj_geom['Vectors'][i,5] + factor[0] * proj_geom['Vectors'][i,8] + factor[1] * proj_geom['Vectors'][i,11];
+ else:
+ raise ValueError('No suitable geometry for postalignment: ' + proj_geom['type'])
+
+ return proj_geom
+
diff --git a/python/astra/pythonutils.py b/python/astra/pythonutils.py
index 27fa8fd..1028a0a 100644
--- a/python/astra/pythonutils.py
+++ b/python/astra/pythonutils.py
@@ -51,7 +51,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'] == 'fanflat_vec' or geom['type'] == 'parallel_vec':
s = (geom['Vectors'].shape[0], geom['DetectorCount'])
elif geom['type'] == 'parallel3d_vec' or geom['type'] == 'cone_vec':
s = (geom['DetectorRowCount'], geom[
diff --git a/samples/matlab/s014_FBP.m b/samples/matlab/s014_FBP.m
index 4d1de13..fb91a41 100644
--- a/samples/matlab/s014_FBP.m
+++ b/samples/matlab/s014_FBP.m
@@ -9,7 +9,7 @@
% -----------------------------------------------------------------------
vol_geom = astra_create_vol_geom(256, 256);
-proj_geom = astra_create_proj_geom('parallel', 1.0, 384, linspace2(0,pi,180));
+proj_geom = astra_create_proj_geom('fanflat', 1.0, 384, linspace2(0,2*pi,1800), 500, 0);
% As before, create a sinogram from a phantom
P = phantom(256);
diff --git a/samples/matlab/s022_fbp_cor.m b/samples/matlab/s022_fbp_cor.m
new file mode 100644
index 0000000..b33d5ce
--- /dev/null
+++ b/samples/matlab/s022_fbp_cor.m
@@ -0,0 +1,69 @@
+% -----------------------------------------------------------------------
+% This file is part of the ASTRA Toolbox
+%
+% Copyright: 2010-2016, iMinds-Vision Lab, University of Antwerp
+% 2014-2016, CWI, Amsterdam
+% License: Open Source under GPLv3
+% Contact: astra@uantwerpen.be
+% Website: http://www.astra-toolbox.com/
+% -----------------------------------------------------------------------
+
+cor_shift = 3.6;
+
+vol_geom = astra_create_vol_geom(256, 256);
+proj_geom = astra_create_proj_geom('parallel', 1.0, 256, linspace2(0,pi,180));
+
+% Projection geometry with shifted center of rotation
+% We create this by shifting the detector center V(:,3:4) by a multiple of the detector
+% orientation V(:,5:6).
+proj_geom_cor = astra_geom_2vec(proj_geom);
+proj_geom_cor.Vectors(:,3:4) = proj_geom_cor.Vectors(:,3:4) + cor_shift * proj_geom_cor.Vectors(:,5:6);
+
+% As before, create a sinogram from a phantom, using the shifted center of rotation
+P = phantom(256);
+[sinogram_id, sinogram] = astra_create_sino_gpu(P, proj_geom_cor, vol_geom);
+figure(1); imshow(P, []);
+figure(2); imshow(sinogram, []);
+
+astra_mex_data2d('delete', sinogram_id);
+
+% We now re-create the sinogram data object as we would do when loading
+% an external sinogram, using a standard geometry, and try to do a reconstruction,
+% to show the misalignment artifacts caused by the shifted center of rotation
+sinogram_id = astra_mex_data2d('create', '-sino', proj_geom, sinogram);
+
+% Create a data object for the reconstruction
+rec_id = astra_mex_data2d('create', '-vol', vol_geom);
+
+% Set up the parameters for a reconstruction algorithm using the GPU
+cfg = astra_struct('FBP_CUDA');
+cfg.ReconstructionDataId = rec_id;
+cfg.ProjectionDataId = sinogram_id;
+alg_id = astra_mex_algorithm('create', cfg);
+astra_mex_algorithm('run', alg_id);
+
+% Get the result
+rec = astra_mex_data2d('get', rec_id);
+figure(3); imshow(rec, []);
+
+astra_mex_algorithm('delete', alg_id);
+astra_mex_data2d('delete', rec_id);
+
+% Now change back to the proper, shifted geometry, and do another reconstruction
+astra_mex_data2d('change_geometry', sinogram_id, proj_geom_cor);
+rec_id = astra_mex_data2d('create', '-vol', vol_geom);
+
+cfg = astra_struct('FBP_CUDA');
+cfg.ReconstructionDataId = rec_id;
+cfg.ProjectionDataId = sinogram_id;
+alg_id = astra_mex_algorithm('create', cfg);
+astra_mex_algorithm('run', alg_id);
+
+% Get the result
+rec = astra_mex_data2d('get', rec_id);
+figure(4); imshow(rec, []);
+
+
+astra_mex_algorithm('delete', alg_id);
+astra_mex_data2d('delete', rec_id);
+astra_mex_data2d('delete', sinogram_id);
diff --git a/samples/python/s022_fbp_cor.py b/samples/python/s022_fbp_cor.py
new file mode 100644
index 0000000..eb2f56b
--- /dev/null
+++ b/samples/python/s022_fbp_cor.py
@@ -0,0 +1,98 @@
+# -----------------------------------------------------------------------
+# Copyright: 2010-2016, iMinds-Vision Lab, University of Antwerp
+# 2013-2016, CWI, Amsterdam
+#
+# Contact: astra@uantwerpen.be
+# Website: http://www.astra-toolbox.com/
+#
+# This file is part of the ASTRA Toolbox.
+#
+#
+# The ASTRA Toolbox is free software: you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation, either version 3 of the License, or
+# (at your option) any later version.
+#
+# The ASTRA Toolbox is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with the ASTRA Toolbox. If not, see <http://www.gnu.org/licenses/>.
+#
+# -----------------------------------------------------------------------
+
+import astra
+import numpy as np
+
+cor_shift = 3.6
+
+vol_geom = astra.create_vol_geom(256, 256)
+proj_geom = astra.create_proj_geom('parallel', 1.0, 256, np.linspace(0,np.pi,180,False))
+
+# Projection geometry with shifted center of rotation
+# We create this by shifting the detector center V[:,2:4] by a multiple of the detector
+# orientation V[:,4:6].
+proj_geom_cor = astra.functions.geom_2vec(proj_geom)
+V = proj_geom_cor['Vectors']
+V[:,2:4] = V[:,2:4] + cor_shift * V[:,4:6]
+
+# As before, create a sinogram from a phantom, using the shifted center of rotation
+import scipy.io
+P = scipy.io.loadmat('phantom.mat')['phantom256']
+
+proj_id_cor = astra.create_projector('cuda',proj_geom_cor,vol_geom)
+sinogram_id, sinogram = astra.create_sino(P, proj_id_cor)
+
+# Change the projection geometry metadata attached to the sinogram to standard geometry,
+# and try to do a reconstruction, to show the misalignment artifacts caused by
+# the shifted center of rotation
+astra.data2d.change_geometry(sinogram_id, proj_geom)
+
+import pylab
+pylab.gray()
+pylab.figure(1)
+pylab.imshow(P)
+pylab.figure(2)
+pylab.imshow(sinogram)
+
+# Create a data object for the reconstruction
+rec_id = astra.data2d.create('-vol', vol_geom)
+
+# Set up the parameters for a reconstruction algorithm using the GPU
+cfg = astra.astra_dict('FBP_CUDA')
+cfg['ReconstructionDataId'] = rec_id
+cfg['ProjectionDataId'] = sinogram_id
+alg_id = astra.algorithm.create(cfg)
+astra.algorithm.run(alg_id)
+
+# Get the result
+rec = astra.data2d.get(rec_id)
+pylab.figure(3)
+pylab.imshow(rec)
+
+astra.algorithm.delete(alg_id)
+astra.data2d.delete(rec_id)
+
+# Now change back to the proper, shifted geometry, and do another reconstruction
+astra.data2d.change_geometry(sinogram_id, proj_geom_cor)
+rec_id = astra.data2d.create('-vol', vol_geom)
+cfg = astra.astra_dict('FBP_CUDA')
+cfg['ReconstructionDataId'] = rec_id
+cfg['ProjectionDataId'] = sinogram_id
+alg_id = astra.algorithm.create(cfg)
+astra.algorithm.run(alg_id)
+
+# Get the result
+rec = astra.data2d.get(rec_id)
+pylab.figure(4)
+pylab.imshow(rec)
+pylab.show()
+
+
+
+astra.data2d.delete(sinogram_id)
+astra.projector.delete(proj_id_cor)
+astra.algorithm.delete(alg_id)
+astra.data2d.delete(rec_id)
diff --git a/src/ConeProjectionGeometry3D.cpp b/src/ConeProjectionGeometry3D.cpp
index 217a916..e8b95f1 100644
--- a/src/ConeProjectionGeometry3D.cpp
+++ b/src/ConeProjectionGeometry3D.cpp
@@ -225,7 +225,7 @@ CVector3D CConeProjectionGeometry3D::getProjectionDirection(int _iProjectionInde
#undef ROTATE
- CVector3D ret(fDetX - fSrcX, fDetY - fSrcY, fDetZ - fDetZ);
+ CVector3D ret(fDetX - fSrcX, fDetY - fSrcY, fDetZ - fSrcZ);
return ret;
}
diff --git a/src/CudaFDKAlgorithm3D.cpp b/src/CudaFDKAlgorithm3D.cpp
index e7c65be..f94a24f 100644
--- a/src/CudaFDKAlgorithm3D.cpp
+++ b/src/CudaFDKAlgorithm3D.cpp
@@ -156,7 +156,7 @@ bool CCudaFDKAlgorithm3D::initialize(const Config& _cfg)
const CProjectionGeometry3D* projgeom = m_pSinogram->getGeometry();
const CProjectionGeometry2D* filtgeom = pFilterData->getGeometry();
int iPaddedDetCount = calcNextPowerOfTwo(2 * projgeom->getDetectorColCount());
- int iHalfFFTSize = calcFFTFourSize(iPaddedDetCount);
+ int iHalfFFTSize = astraCUDA::calcFFTFourierSize(iPaddedDetCount);
if(filtgeom->getDetectorCount()!=iHalfFFTSize || filtgeom->getProjectionAngleCount()!=projgeom->getProjectionCount()){
ASTRA_ERROR("Filter size does not match required size (%i angles, %i detectors)",projgeom->getProjectionCount(),iHalfFFTSize);
return false;
diff --git a/src/CudaFilteredBackProjectionAlgorithm.cpp b/src/CudaFilteredBackProjectionAlgorithm.cpp
index a5d7b93..f3cca12 100644
--- a/src/CudaFilteredBackProjectionAlgorithm.cpp
+++ b/src/CudaFilteredBackProjectionAlgorithm.cpp
@@ -32,6 +32,7 @@ along with the ASTRA Toolbox. If not, see <http://www.gnu.org/licenses/>.
#include "astra/AstraObjectManager.h"
#include "astra/CudaProjector2D.h"
#include "../cuda/2d/astra.h"
+#include "../cuda/2d/fbp.h"
#include "astra/Logging.h"
@@ -43,8 +44,7 @@ string CCudaFilteredBackProjectionAlgorithm::type = "FBP_CUDA";
CCudaFilteredBackProjectionAlgorithm::CCudaFilteredBackProjectionAlgorithm()
{
m_bIsInitialized = false;
- CReconstructionAlgorithm2D::_clear();
- m_pFBP = 0;
+ CCudaReconstructionAlgorithm2D::_clear();
m_pfFilter = NULL;
m_fFilterParameter = -1.0f;
m_fFilterD = 1.0f;
@@ -52,35 +52,8 @@ CCudaFilteredBackProjectionAlgorithm::CCudaFilteredBackProjectionAlgorithm()
CCudaFilteredBackProjectionAlgorithm::~CCudaFilteredBackProjectionAlgorithm()
{
- if(m_pfFilter != NULL)
- {
- delete [] m_pfFilter;
- m_pfFilter = NULL;
- }
-
- if(m_pFBP != NULL)
- {
- delete m_pFBP;
- m_pFBP = NULL;
- }
-}
-
-void CCudaFilteredBackProjectionAlgorithm::initializeFromProjector()
-{
- m_iPixelSuperSampling = 1;
- m_iGPUIndex = -1;
-
- // Projector
- CCudaProjector2D* pCudaProjector = dynamic_cast<CCudaProjector2D*>(m_pProjector);
- if (!pCudaProjector) {
- if (m_pProjector) {
- ASTRA_WARN("non-CUDA Projector2D passed to FBP_CUDA");
- }
- } else {
- m_iPixelSuperSampling = pCudaProjector->getVoxelSuperSampling();
- m_iGPUIndex = pCudaProjector->getGPUIndex();
- }
-
+ delete[] m_pfFilter;
+ m_pfFilter = NULL;
}
bool CCudaFilteredBackProjectionAlgorithm::initialize(const Config& _cfg)
@@ -94,51 +67,24 @@ bool CCudaFilteredBackProjectionAlgorithm::initialize(const Config& _cfg)
clear();
}
- // Projector
- XMLNode node = _cfg.self.getSingleNode("ProjectorId");
- CCudaProjector2D* pCudaProjector = 0;
- if (node) {
- int id = node.getContentInt();
- CProjector2D *projector = CProjector2DManager::getSingleton().get(id);
- pCudaProjector = dynamic_cast<CCudaProjector2D*>(projector);
- if (!pCudaProjector) {
- ASTRA_WARN("non-CUDA Projector2D passed");
- }
- }
- CC.markNodeParsed("ProjectorId");
-
-
- // sinogram data
- node = _cfg.self.getSingleNode("ProjectionDataId");
- ASTRA_CONFIG_CHECK(node, "CudaFBP", "No ProjectionDataId tag specified.");
- int id = node.getContentInt();
- m_pSinogram = dynamic_cast<CFloat32ProjectionData2D*>(CData2DManager::getSingleton().get(id));
- CC.markNodeParsed("ProjectionDataId");
+ m_bIsInitialized = CCudaReconstructionAlgorithm2D::initialize(_cfg);
+ if (!m_bIsInitialized)
+ return false;
- // reconstruction data
- node = _cfg.self.getSingleNode("ReconstructionDataId");
- ASTRA_CONFIG_CHECK(node, "CudaFBP", "No ReconstructionDataId tag specified.");
- id = node.getContentInt();
- m_pReconstruction = dynamic_cast<CFloat32VolumeData2D*>(CData2DManager::getSingleton().get(id));
- CC.markNodeParsed("ReconstructionDataId");
// filter type
- node = _cfg.self.getSingleNode("FilterType");
+ XMLNode node = _cfg.self.getSingleNode("FilterType");
if (node)
- {
m_eFilter = _convertStringToFilter(node.getContent().c_str());
- }
else
- {
m_eFilter = FILTER_RAMLAK;
- }
CC.markNodeParsed("FilterType");
// filter
node = _cfg.self.getSingleNode("FilterSinogramId");
if (node)
{
- id = node.getContentInt();
+ int id = node.getContentInt();
const CFloat32ProjectionData2D * pFilterData = dynamic_cast<CFloat32ProjectionData2D*>(CData2DManager::getSingleton().get(id));
m_iFilterWidth = pFilterData->getGeometry()->getDetectorCount();
int iFilterProjectionCount = pFilterData->getGeometry()->getProjectionAngleCount();
@@ -187,20 +133,9 @@ bool CCudaFilteredBackProjectionAlgorithm::initialize(const Config& _cfg)
initializeFromProjector();
- // Deprecated options
- m_iPixelSuperSampling = (int)_cfg.self.getOptionNumerical("PixelSuperSampling", m_iPixelSuperSampling);
- CC.markOptionParsed("PixelSuperSampling");
- // GPU number
- m_iGPUIndex = (int)_cfg.self.getOptionNumerical("GPUindex", -1);
- m_iGPUIndex = (int)_cfg.self.getOptionNumerical("GPUIndex", m_iGPUIndex);
- CC.markOptionParsed("GPUIndex");
- if (!_cfg.self.hasOption("GPUIndex"))
- CC.markOptionParsed("GPUindex");
-
-
- m_pFBP = new AstraFBP;
- m_bAstraFBPInit = false;
+ m_pAlgo = new astraCUDA::FBP();
+ m_bAlgoInit = false;
return check();
}
@@ -225,9 +160,8 @@ bool CCudaFilteredBackProjectionAlgorithm::initialize(CFloat32ProjectionData2D *
// success
m_bIsInitialized = true;
- m_pFBP = new AstraFBP;
-
- m_bAstraFBPInit = false;
+ m_pAlgo = new astraCUDA::FBP();
+ m_bAlgoInit = false;
if(_pfFilter != NULL)
{
@@ -255,107 +189,26 @@ bool CCudaFilteredBackProjectionAlgorithm::initialize(CFloat32ProjectionData2D *
return check();
}
-void CCudaFilteredBackProjectionAlgorithm::run(int _iNrIterations /* = 0 */)
-{
- // check initialized
- ASTRA_ASSERT(m_bIsInitialized);
-
- if (!m_bAstraFBPInit) {
-
- const CVolumeGeometry2D& volgeom = *m_pReconstruction->getGeometry();
- const CParallelProjectionGeometry2D* parprojgeom = dynamic_cast<CParallelProjectionGeometry2D*>(m_pSinogram->getGeometry());
- const CFanFlatProjectionGeometry2D* fanprojgeom = dynamic_cast<CFanFlatProjectionGeometry2D*>(m_pSinogram->getGeometry());
-
- bool ok = true;
-
- // TODO: non-square pixels?
- ok &= m_pFBP->setReconstructionGeometry(volgeom.getGridColCount(),
- volgeom.getGridRowCount(),
- volgeom.getPixelLengthX());
- int iDetectorCount;
- if (parprojgeom) {
-
- float *offsets, *angles, detSize, outputScale;
-
- ok = convertAstraGeometry(&volgeom, parprojgeom, offsets, angles, detSize, outputScale);
-
-
- ok &= m_pFBP->setProjectionGeometry(parprojgeom->getProjectionAngleCount(),
- parprojgeom->getDetectorCount(),
- angles,
- parprojgeom->getDetectorWidth());
- iDetectorCount = parprojgeom->getDetectorCount();
-
- // TODO: Are detSize and outputScale handled correctly?
- if (offsets)
- ok &= m_pFBP->setTOffsets(offsets);
- ASTRA_ASSERT(ok);
-
- delete[] offsets;
- delete[] angles;
-
- } else if (fanprojgeom) {
-
- astraCUDA::SFanProjection* projs;
- float outputScale;
-
- // FIXME: Implement this, and clean up the interface to AstraFBP.
- if (abs(volgeom.getWindowMinX() + volgeom.getWindowMaxX()) > 0.00001 * volgeom.getPixelLengthX()) {
- // Off-center volume geometry isn't supported yet
- ASTRA_ASSERT(false);
- }
- if (abs(volgeom.getWindowMinY() + volgeom.getWindowMaxY()) > 0.00001 * volgeom.getPixelLengthY()) {
- // Off-center volume geometry isn't supported yet
- ASTRA_ASSERT(false);
- }
-
- ok = convertAstraGeometry(&volgeom, fanprojgeom, projs, outputScale);
-
- // CHECKME: outputScale?
-
- ok &= m_pFBP->setFanGeometry(fanprojgeom->getProjectionAngleCount(),
- fanprojgeom->getDetectorCount(),
- projs,
- fanprojgeom->getProjectionAngles(),
- fanprojgeom->getOriginSourceDistance(),
- fanprojgeom->getOriginDetectorDistance(),
-
- fanprojgeom->getDetectorWidth(),
- m_bShortScan);
-
- iDetectorCount = fanprojgeom->getDetectorCount();
-
- delete[] projs;
- } else {
- assert(false);
- }
-
- ok &= m_pFBP->setPixelSuperSampling(m_iPixelSuperSampling);
-
- ASTRA_ASSERT(ok);
-
- ok &= m_pFBP->init(m_iGPUIndex);
- ASTRA_ASSERT(ok);
+void CCudaFilteredBackProjectionAlgorithm::initCUDAAlgorithm()
+{
+ CCudaReconstructionAlgorithm2D::initCUDAAlgorithm();
- ok &= m_pFBP->setSinogram(m_pSinogram->getDataConst(), iDetectorCount);
- ASTRA_ASSERT(ok);
+ astraCUDA::FBP* pFBP = dynamic_cast<astraCUDA::FBP*>(m_pAlgo);
- ok &= m_pFBP->setFilter(m_eFilter, m_pfFilter, m_iFilterWidth, m_fFilterD, m_fFilterParameter);
+ bool ok = pFBP->setFilter(m_eFilter, m_pfFilter, m_iFilterWidth, m_fFilterD, m_fFilterParameter);
+ if (!ok) {
+ ASTRA_ERROR("CCudaFilteredBackProjectionAlgorithm: Failed to set filter");
ASTRA_ASSERT(ok);
-
- m_bAstraFBPInit = true;
}
- bool ok = m_pFBP->run();
- ASTRA_ASSERT(ok);
-
- const CVolumeGeometry2D& volgeom = *m_pReconstruction->getGeometry();
- ok &= m_pFBP->getReconstruction(m_pReconstruction->getData(), volgeom.getGridColCount());
-
- ASTRA_ASSERT(ok);
+ ok &= pFBP->setShortScan(m_bShortScan);
+ if (!ok) {
+ ASTRA_ERROR("CCudaFilteredBackProjectionAlgorithm: Failed to set short-scan mode");
+ }
}
+
bool CCudaFilteredBackProjectionAlgorithm::check()
{
// check pointers
@@ -382,28 +235,6 @@ bool CCudaFilteredBackProjectionAlgorithm::check()
return true;
}
-static int calcNextPowerOfTwo(int _iValue)
-{
- int iOutput = 1;
-
- while(iOutput < _iValue)
- {
- iOutput *= 2;
- }
-
- return iOutput;
-}
-
-int CCudaFilteredBackProjectionAlgorithm::calcIdealRealFilterWidth(int _iDetectorCount)
-{
- return calcNextPowerOfTwo(_iDetectorCount);
-}
-
-int CCudaFilteredBackProjectionAlgorithm::calcIdealFourierFilterWidth(int _iDetectorCount)
-{
- return (calcNextPowerOfTwo(_iDetectorCount) / 2 + 1);
-}
-
static bool stringCompareLowerCase(const char * _stringA, const char * _stringB)
{
int iCmpReturn = 0;
diff --git a/src/CudaForwardProjectionAlgorithm.cpp b/src/CudaForwardProjectionAlgorithm.cpp
index bac1251..f4cafe6 100644
--- a/src/CudaForwardProjectionAlgorithm.cpp
+++ b/src/CudaForwardProjectionAlgorithm.cpp
@@ -220,56 +220,48 @@ void CCudaForwardProjectionAlgorithm::run(int)
// check initialized
assert(m_bIsInitialized);
- CVolumeGeometry2D* pVolGeom = m_pVolume->getGeometry();
- const CParallelProjectionGeometry2D* parProjGeom = dynamic_cast<CParallelProjectionGeometry2D*>(m_pSinogram->getGeometry());
- const CFanFlatProjectionGeometry2D* fanProjGeom = dynamic_cast<CFanFlatProjectionGeometry2D*>(m_pSinogram->getGeometry());
- const CFanFlatVecProjectionGeometry2D* fanVecProjGeom = dynamic_cast<CFanFlatVecProjectionGeometry2D*>(m_pSinogram->getGeometry());
+ bool ok;
- bool ok = false;
- if (parProjGeom) {
+ const CVolumeGeometry2D* pVolGeom = m_pVolume->getGeometry();
+ const CProjectionGeometry2D* pProjGeom = m_pSinogram->getGeometry();
+ astraCUDA::SDimensions dims;
- float *offsets, *angles, detSize, outputScale;
- ok = convertAstraGeometry(pVolGeom, parProjGeom, offsets, angles, detSize, outputScale);
+ ok = convertAstraGeometry_dims(pVolGeom, pProjGeom, dims);
- ASTRA_ASSERT(ok); // FIXME
+ if (!ok)
+ return;
- // FIXME: Output scaling
+ astraCUDA::SParProjection* pParProjs = 0;
+ astraCUDA::SFanProjection* pFanProjs = 0;
+ float fOutputScale = 1.0f;
- ok = astraCudaFP(m_pVolume->getDataConst(), m_pSinogram->getData(),
- pVolGeom->getGridColCount(), pVolGeom->getGridRowCount(),
- parProjGeom->getProjectionAngleCount(),
- parProjGeom->getDetectorCount(),
- angles, offsets, detSize,
- m_iDetectorSuperSampling, 1.0f * outputScale, m_iGPUIndex);
-
- delete[] offsets;
- delete[] angles;
+ ok = convertAstraGeometry(pVolGeom, pProjGeom, pParProjs, pFanProjs, fOutputScale);
+ if (!ok)
+ return;
- } else if (fanProjGeom || fanVecProjGeom) {
+ if (pParProjs) {
+ assert(!pFanProjs);
- astraCUDA::SFanProjection* projs;
- float outputScale;
+ ok = astraCudaFP(m_pVolume->getDataConst(), m_pSinogram->getData(),
+ pVolGeom->getGridColCount(), pVolGeom->getGridRowCount(),
+ pProjGeom->getProjectionAngleCount(),
+ pProjGeom->getDetectorCount(),
+ pParProjs,
+ m_iDetectorSuperSampling, 1.0f * fOutputScale, m_iGPUIndex);
- if (fanProjGeom) {
- ok = convertAstraGeometry(pVolGeom, fanProjGeom, projs, outputScale);
- } else {
- ok = convertAstraGeometry(pVolGeom, fanVecProjGeom, projs, outputScale);
- }
+ delete[] pParProjs;
- ASTRA_ASSERT(ok);
+ } else {
+ assert(pFanProjs);
ok = astraCudaFanFP(m_pVolume->getDataConst(), m_pSinogram->getData(),
pVolGeom->getGridColCount(), pVolGeom->getGridRowCount(),
- m_pSinogram->getGeometry()->getProjectionAngleCount(),
- m_pSinogram->getGeometry()->getDetectorCount(),
- projs,
- m_iDetectorSuperSampling, outputScale, m_iGPUIndex);
-
- delete[] projs;
-
- } else {
+ pProjGeom->getProjectionAngleCount(),
+ pProjGeom->getDetectorCount(),
+ pFanProjs,
+ m_iDetectorSuperSampling, fOutputScale, m_iGPUIndex);
- ASTRA_ASSERT(false);
+ delete[] pFanProjs;
}
diff --git a/src/CudaReconstructionAlgorithm2D.cpp b/src/CudaReconstructionAlgorithm2D.cpp
index 96f52f0..f37647c 100644
--- a/src/CudaReconstructionAlgorithm2D.cpp
+++ b/src/CudaReconstructionAlgorithm2D.cpp
@@ -249,69 +249,14 @@ bool CCudaReconstructionAlgorithm2D::setupGeometry()
ok = m_pAlgo->setGPUIndex(m_iGPUIndex);
if (!ok) return false;
- astraCUDA::SDimensions dims;
-
const CVolumeGeometry2D& volgeom = *m_pReconstruction->getGeometry();
+ const CProjectionGeometry2D& projgeom = *m_pSinogram->getGeometry();
- // TODO: non-square pixels?
- dims.iVolWidth = volgeom.getGridColCount();
- dims.iVolHeight = volgeom.getGridRowCount();
- float fPixelSize = volgeom.getPixelLengthX();
-
- dims.iRaysPerDet = m_iDetectorSuperSampling;
- dims.iRaysPerPixelDim = m_iPixelSuperSampling;
-
-
- const CParallelProjectionGeometry2D* parProjGeom = dynamic_cast<CParallelProjectionGeometry2D*>(m_pSinogram->getGeometry());
- const CFanFlatProjectionGeometry2D* fanProjGeom = dynamic_cast<CFanFlatProjectionGeometry2D*>(m_pSinogram->getGeometry());
- const CFanFlatVecProjectionGeometry2D* fanVecProjGeom = dynamic_cast<CFanFlatVecProjectionGeometry2D*>(m_pSinogram->getGeometry());
-
- if (parProjGeom) {
-
- float *offsets, *angles, detSize, outputScale;
-
- ok = convertAstraGeometry(&volgeom, parProjGeom, offsets, angles, detSize, outputScale);
-
- dims.iProjAngles = parProjGeom->getProjectionAngleCount();
- dims.iProjDets = parProjGeom->getDetectorCount();
- dims.fDetScale = parProjGeom->getDetectorWidth() / fPixelSize;
-
- ok = m_pAlgo->setGeometry(dims, parProjGeom->getProjectionAngles());
- ok &= m_pAlgo->setTOffsets(offsets);
-
- // CHECKME: outputScale? detSize?
-
- delete[] offsets;
- delete[] angles;
-
- } else if (fanProjGeom || fanVecProjGeom) {
-
- astraCUDA::SFanProjection* projs;
- float outputScale;
-
- if (fanProjGeom) {
- ok = convertAstraGeometry(&volgeom, fanProjGeom, projs, outputScale);
- } else {
- ok = convertAstraGeometry(&volgeom, fanVecProjGeom, projs, outputScale);
- }
-
- dims.iProjAngles = m_pSinogram->getGeometry()->getProjectionAngleCount();
- dims.iProjDets = m_pSinogram->getGeometry()->getDetectorCount();
- dims.fDetScale = m_pSinogram->getGeometry()->getDetectorWidth() / fPixelSize;
-
- ok = m_pAlgo->setFanGeometry(dims, projs);
-
- // CHECKME: outputScale?
-
- delete[] projs;
-
- } else {
-
- ASTRA_ASSERT(false);
-
- }
+ ok = m_pAlgo->setGeometry(&volgeom, &projgeom);
if (!ok) return false;
+ ok = m_pAlgo->setSuperSampling(m_iDetectorSuperSampling, m_iPixelSuperSampling);
+ if (!ok) return false;
if (m_bUseReconstructionMask)
ok &= m_pAlgo->enableVolumeMask();
diff --git a/src/FanFlatBeamLineKernelProjector2D.cpp b/src/FanFlatBeamLineKernelProjector2D.cpp
index 4bfff58..7bec351 100644
--- a/src/FanFlatBeamLineKernelProjector2D.cpp
+++ b/src/FanFlatBeamLineKernelProjector2D.cpp
@@ -91,7 +91,7 @@ bool CFanFlatBeamLineKernelProjector2D::_check()
ASTRA_CONFIG_CHECK(dynamic_cast<CFanFlatProjectionGeometry2D*>(m_pProjectionGeometry) || dynamic_cast<CFanFlatVecProjectionGeometry2D*>(m_pProjectionGeometry), "FanFlatBeamLineKernelProjector2D", "Unsupported projection geometry");
- ASTRA_CONFIG_CHECK(m_pVolumeGeometry->getPixelLengthX() == m_pVolumeGeometry->getPixelLengthY(), "FanFlatBeamLineKernelProjector2D", "Pixel height must equal pixel width.");
+ ASTRA_CONFIG_CHECK(abs(m_pVolumeGeometry->getPixelLengthX() / m_pVolumeGeometry->getPixelLengthY()) - 1 < eps, "FanFlatBeamLineKernelProjector2D", "Pixel height must equal pixel width.");
// success
return true;
diff --git a/src/FanFlatBeamStripKernelProjector2D.cpp b/src/FanFlatBeamStripKernelProjector2D.cpp
index 198c0ea..de12d49 100644
--- a/src/FanFlatBeamStripKernelProjector2D.cpp
+++ b/src/FanFlatBeamStripKernelProjector2D.cpp
@@ -89,7 +89,7 @@ bool CFanFlatBeamStripKernelProjector2D::_check()
ASTRA_CONFIG_CHECK(dynamic_cast<CFanFlatProjectionGeometry2D*>(m_pProjectionGeometry), "FanFlatBeamLineKernelProjector2D", "Unsupported projection geometry");
- ASTRA_CONFIG_CHECK(m_pVolumeGeometry->getPixelLengthX() == m_pVolumeGeometry->getPixelLengthY(), "FanFlatBeamStripKernelProjector2D", "Pixel height must equal pixel width.");
+ ASTRA_CONFIG_CHECK(abs(m_pVolumeGeometry->getPixelLengthX() / m_pVolumeGeometry->getPixelLengthY()) - 1 < eps, "FanFlatBeamStripKernelProjector2D", "Pixel height must equal pixel width.");
// success
return true;
diff --git a/src/FanFlatProjectionGeometry2D.cpp b/src/FanFlatProjectionGeometry2D.cpp
index 28bc75e..697550c 100644
--- a/src/FanFlatProjectionGeometry2D.cpp
+++ b/src/FanFlatProjectionGeometry2D.cpp
@@ -27,6 +27,8 @@ along with the ASTRA Toolbox. If not, see <http://www.gnu.org/licenses/>.
#include "astra/FanFlatProjectionGeometry2D.h"
+#include "astra/GeometryUtil2D.h"
+
#include <cstring>
#include <sstream>
@@ -213,7 +215,21 @@ Config* CFanFlatProjectionGeometry2D::getConfiguration() const
cfg->self.addChildNode("ProjectionAngles", m_pfProjectionAngles, m_iProjectionAngleCount);
return cfg;
}
-//----------------------------------------------------------------------------------------
+//----------------------------------------------------------------------------------------
+CFanFlatVecProjectionGeometry2D* CFanFlatProjectionGeometry2D::toVectorGeometry()
+{
+ SFanProjection* vectors = genFanProjections(m_iProjectionAngleCount,
+ m_iDetectorCount,
+ m_fOriginSourceDistance,
+ m_fOriginDetectorDistance,
+ m_fDetectorWidth,
+ m_pfProjectionAngles);
+
+ CFanFlatVecProjectionGeometry2D* vecGeom = new CFanFlatVecProjectionGeometry2D();
+ vecGeom->initialize(m_iProjectionAngleCount, m_iDetectorCount, vectors);
+ delete[] vectors;
+ return vecGeom;
+}
} // namespace astra
diff --git a/src/Float32Data2D.cpp b/src/Float32Data2D.cpp
index 56ea7cc..c5d2cc8 100644
--- a/src/Float32Data2D.cpp
+++ b/src/Float32Data2D.cpp
@@ -286,7 +286,7 @@ void CFloat32Data2D::_allocateData()
ASTRA_ASSERT(!m_bInitialized);
ASTRA_ASSERT(m_iSize > 0);
- ASTRA_ASSERT(m_iSize == (size_t)m_iWidth * m_iHeight);
+ ASTRA_ASSERT((size_t)m_iSize == (size_t)m_iWidth * m_iHeight);
ASTRA_ASSERT(m_pfData == NULL);
ASTRA_ASSERT(m_ppfData2D == NULL);
diff --git a/src/GeometryUtil2D.cpp b/src/GeometryUtil2D.cpp
new file mode 100644
index 0000000..2ee6185
--- /dev/null
+++ b/src/GeometryUtil2D.cpp
@@ -0,0 +1,175 @@
+/*
+-----------------------------------------------------------------------
+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/GeometryUtil2D.h"
+
+#include <cmath>
+
+namespace astra {
+
+SParProjection* genParProjections(unsigned int iProjAngles,
+ unsigned int iProjDets,
+ double fDetSize,
+ const float *pfAngles,
+ const float *pfExtraOffsets)
+{
+ SParProjection base;
+ base.fRayX = 0.0f;
+ base.fRayY = 1.0f;
+
+ base.fDetSX = iProjDets * fDetSize * -0.5f;
+ base.fDetSY = 0.0f;
+
+ base.fDetUX = fDetSize;
+ base.fDetUY = 0.0f;
+
+ SParProjection* p = new SParProjection[iProjAngles];
+
+#define ROTATE0(name,i,alpha) do { p[i].f##name##X = base.f##name##X * cos(alpha) - base.f##name##Y * sin(alpha); p[i].f##name##Y = base.f##name##X * sin(alpha) + base.f##name##Y * cos(alpha); } while(0)
+
+ for (unsigned int i = 0; i < iProjAngles; ++i) {
+ if (pfExtraOffsets) {
+ // TODO
+ }
+
+ ROTATE0(Ray, i, pfAngles[i]);
+ ROTATE0(DetS, i, pfAngles[i]);
+ ROTATE0(DetU, i, pfAngles[i]);
+
+ if (pfExtraOffsets) {
+ float d = pfExtraOffsets[i];
+ p[i].fDetSX -= d * p[i].fDetUX;
+ p[i].fDetSY -= d * p[i].fDetUY;
+ }
+ }
+
+#undef ROTATE0
+
+ return p;
+}
+
+
+SFanProjection* genFanProjections(unsigned int iProjAngles,
+ unsigned int iProjDets,
+ double fOriginSource, double fOriginDetector,
+ double fDetSize,
+ const float *pfAngles)
+// const float *pfExtraOffsets)
+{
+ SFanProjection *pProjs = new SFanProjection[iProjAngles];
+
+ float fSrcX0 = 0.0f;
+ float fSrcY0 = -fOriginSource;
+ float fDetUX0 = fDetSize;
+ float fDetUY0 = 0.0f;
+ float fDetSX0 = iProjDets * fDetUX0 / -2.0f;
+ float fDetSY0 = fOriginDetector;
+
+#define ROTATE0(name,i,alpha) do { pProjs[i].f##name##X = f##name##X0 * cos(alpha) - f##name##Y0 * sin(alpha); pProjs[i].f##name##Y = f##name##X0 * sin(alpha) + f##name##Y0 * cos(alpha); } while(0)
+ for (unsigned int i = 0; i < iProjAngles; ++i) {
+ ROTATE0(Src, i, pfAngles[i]);
+ ROTATE0(DetS, i, pfAngles[i]);
+ ROTATE0(DetU, i, pfAngles[i]);
+ }
+
+#undef ROTATE0
+
+ return pProjs;
+}
+
+// Convert a SParProjection back into its set of "standard" circular parallel
+// beam parameters. This is always possible.
+bool getParParameters(const SParProjection &proj, unsigned int iProjDets, float &fAngle, float &fDetSize, float &fOffset)
+{
+ // Take part of DetU orthogonal to Ray
+ double ux = proj.fDetUX;
+ double uy = proj.fDetUY;
+
+ double t = (ux * proj.fRayX + uy * proj.fRayY) / (proj.fRayX * proj.fRayX + proj.fRayY * proj.fRayY);
+
+ ux -= t * proj.fRayX;
+ uy -= t * proj.fRayY;
+
+ double angle = atan2(uy, ux);
+
+ fAngle = (float)angle;
+
+ double norm2 = uy * uy + ux * ux;
+
+ fDetSize = (float)sqrt(norm2);
+
+ // CHECKME: SIGNS?
+ fOffset = (float)(-0.5*iProjDets - (proj.fDetSY*uy + proj.fDetSX*ux) / norm2);
+
+ return true;
+}
+
+// Convert a SFanProjection back into its set of "standard" circular fan beam
+// parameters. This will return false if it can not be represented in this way.
+bool getFanParameters(const SFanProjection &proj, unsigned int iProjDets, float &fAngle, float &fOriginSource, float &fOriginDetector, float &fDetSize, float &fOffset)
+{
+ // angle
+ // det size
+ // offset
+ // origin-source
+ // origin-detector
+
+ // Need to check if line source-origin is orthogonal to vector ux,uy
+ // (including the case source==origin)
+
+ // (equivalent: source and origin project to same point on detector)
+
+ double dp = proj.fSrcX * proj.fDetUX + proj.fSrcY * proj.fDetUY;
+
+ double rel = (proj.fSrcX*proj.fSrcX + proj.fSrcY*proj.fSrcY) * (proj.fDetUX*proj.fDetUX + proj.fDetUY*proj.fDetUY);
+ rel = sqrt(rel);
+
+ if (std::abs(dp) > rel * 0.0001)
+ return false;
+
+ fOriginSource = sqrt(proj.fSrcX*proj.fSrcX + proj.fSrcY*proj.fSrcY);
+
+ fDetSize = sqrt(proj.fDetUX*proj.fDetUX + proj.fDetUY*proj.fDetUY);
+
+ // project origin on detector line ( == project source on detector line)
+
+ double t = (- proj.fDetSX) * proj.fDetUX + (- proj.fDetSY) * proj.fDetUY;
+
+ fOffset = (float)t - 0.5*iProjDets;
+
+ // TODO: CHECKME
+ fOriginDetector = sqrt((proj.fDetSX + t * proj.fDetUX)*(proj.fDetSX + t * proj.fDetUX) + (proj.fDetSY + t * proj.fDetUY)*(proj.fDetSY + t * proj.fDetUY));
+
+ //float fAngle = atan2(proj.fDetSX + t * proj.fDetUX - proj.fSrcX, proj.fDetSY + t * proj.fDetUY); // TODO: Fix order + sign
+ fAngle = atan2(proj.fDetUY, proj.fDetUX); // TODO: Check order + sign
+
+ return true;
+}
+
+
+}
diff --git a/src/ParallelBeamBlobKernelProjector2D.cpp b/src/ParallelBeamBlobKernelProjector2D.cpp
index 5cc9174..7ff13e9 100644
--- a/src/ParallelBeamBlobKernelProjector2D.cpp
+++ b/src/ParallelBeamBlobKernelProjector2D.cpp
@@ -101,7 +101,7 @@ bool CParallelBeamBlobKernelProjector2D::_check()
// check base class
ASTRA_CONFIG_CHECK(CProjector2D::_check(), "ParallelBeamBlobKernelProjector2D", "Error in Projector2D initialization");
- ASTRA_CONFIG_CHECK(dynamic_cast<CParallelProjectionGeometry2D*>(m_pProjectionGeometry), "ParallelBeamBlobKernelProjector2D", "Unsupported projection geometry");
+ ASTRA_CONFIG_CHECK(dynamic_cast<CParallelProjectionGeometry2D*>(m_pProjectionGeometry) || dynamic_cast<CParallelVecProjectionGeometry2D*>(m_pProjectionGeometry), "ParallelBeamBlobKernelProjector2D", "Unsupported projection geometry");
ASTRA_CONFIG_CHECK(m_iBlobSampleCount > 0, "ParallelBeamBlobKernelProjector2D", "m_iBlobSampleCount should be strictly positive.");
ASTRA_CONFIG_CHECK(m_pfBlobValues, "ParallelBeamBlobKernelProjector2D", "Invalid Volume Geometry Object.");
@@ -154,17 +154,6 @@ bool CParallelBeamBlobKernelProjector2D::initialize(const Config& _cfg)
for (int i = 0; i < m_iBlobSampleCount; i++) {
m_pfBlobValues[i] = values[i];
}
-
- // Required: KernelValues
- node2 = node.getSingleNode("KernelValuesNeg");
- ASTRA_CONFIG_CHECK(node2, "BlobProjector", "No Kernel/KernelValuesNeg tag specified.");
- vector<float32> values2 = node2.getContentNumericalArray();
- ASTRA_CONFIG_CHECK(values2.size() == (unsigned int)m_iBlobSampleCount, "BlobProjector", "Number of specified values doesn't match SampleCount.");
- m_pfBlobValuesNeg = new float32[m_iBlobSampleCount];
- for (int i = 0; i < m_iBlobSampleCount; i++) {
- m_pfBlobValuesNeg[i] = values2[i];
- }
-
}
// success
@@ -227,44 +216,44 @@ void CParallelBeamBlobKernelProjector2D::computeSingleRayWeights(int _iProjectio
// Splat a single point
std::vector<SDetector2D> CParallelBeamBlobKernelProjector2D::projectPoint(int _iRow, int _iCol)
{
- float32 x = m_pVolumeGeometry->pixelColToCenterX(_iCol);
- float32 y = m_pVolumeGeometry->pixelRowToCenterY(_iRow);
-
- std::vector<SDetector2D> res;
- // loop projectors and detectors
- for (int iProjection = 0; iProjection < m_pProjectionGeometry->getProjectionAngleCount(); ++iProjection) {
-
- // get projection angle
- float32 theta = m_pProjectionGeometry->getProjectionAngle(iProjection);
- if (theta >= 7*PIdiv4) theta -= 2*PI;
- bool inverse = false;
- if (theta >= 3*PIdiv4) {
- theta -= PI;
- inverse = true;
- }
-
- // calculate distance from the center of the voxel to the ray though the origin
- float32 t = x * cos(theta) + y * sin(theta);
- if (inverse) t *= -1.0f;
-
- // calculate the offset on the detectorarray (in indices)
- float32 d = m_pProjectionGeometry->detectorOffsetToIndexFloat(t);
- int dmin = (int)ceil(d - m_fBlobSize);
- int dmax = (int)floor(d + m_fBlobSize);
-
- // add affected detectors to the list
- for (int i = dmin; i <= dmax; ++i) {
- if (d >= 0 && d < m_pProjectionGeometry->getDetectorCount()) {
- SDetector2D det;
- det.m_iAngleIndex = iProjection;
- det.m_iDetectorIndex = i;
- det.m_iIndex = iProjection * getProjectionGeometry()->getDetectorCount() + i;
- res.push_back(det);
- }
- }
- }
-
- // return result vector
- return res;
-
+ // float32 x = m_pVolumeGeometry->pixelColToCenterX(_iCol);
+ // float32 y = m_pVolumeGeometry->pixelRowToCenterY(_iRow);
+
+ // std::vector<SDetector2D> res;
+ // // loop projectors and detectors
+ // for (int iProjection = 0; iProjection < m_pProjectionGeometry->getProjectionAngleCount(); ++iProjection) {
+
+ // // get projection angle
+ // float32 theta = m_pProjectionGeometry->getProjectionAngle(iProjection);
+ // if (theta >= 7*PIdiv4) theta -= 2*PI;
+ // bool inverse = false;
+ // if (theta >= 3*PIdiv4) {
+ // theta -= PI;
+ // inverse = true;
+ // }
+
+ // // calculate distance from the center of the voxel to the ray though the origin
+ // float32 t = x * cos(theta) + y * sin(theta);
+ // if (inverse) t *= -1.0f;
+
+ // // calculate the offset on the detectorarray (in indices)
+ // float32 d = m_pProjectionGeometry->detectorOffsetToIndexFloat(t);
+ // int dmin = (int)ceil(d - m_fBlobSize);
+ // int dmax = (int)floor(d + m_fBlobSize);
+
+ // // add affected detectors to the list
+ // for (int i = dmin; i <= dmax; ++i) {
+ // if (d >= 0 && d < m_pProjectionGeometry->getDetectorCount()) {
+ // SDetector2D det;
+ // det.m_iAngleIndex = iProjection;
+ // det.m_iDetectorIndex = i;
+ // det.m_iIndex = iProjection * getProjectionGeometry()->getDetectorCount() + i;
+ // res.push_back(det);
+ // }
+ // }
+ // }
+
+ // // return result vector
+ // return res;
+ return std::vector<SDetector2D>();
}
diff --git a/src/ParallelBeamLineKernelProjector2D.cpp b/src/ParallelBeamLineKernelProjector2D.cpp
index a5f46a7..a7d72d4 100644
--- a/src/ParallelBeamLineKernelProjector2D.cpp
+++ b/src/ParallelBeamLineKernelProjector2D.cpp
@@ -87,9 +87,9 @@ 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.");
+ ASTRA_CONFIG_CHECK(abs(m_pVolumeGeometry->getPixelLengthX() / m_pVolumeGeometry->getPixelLengthY()) - 1 < eps, "ParallelBeamLineKernelProjector2D", "Pixel height must equal pixel width.");
// success
return true;
@@ -161,61 +161,63 @@ void CParallelBeamLineKernelProjector2D::computeSingleRayWeights(int _iProjectio
// Project Point
std::vector<SDetector2D> CParallelBeamLineKernelProjector2D::projectPoint(int _iRow, int _iCol)
{
- float32 xUL = m_pVolumeGeometry->pixelColToCenterX(_iCol) - m_pVolumeGeometry->getPixelLengthX() * 0.5f;
- float32 yUL = m_pVolumeGeometry->pixelRowToCenterY(_iRow) - m_pVolumeGeometry->getPixelLengthY() * 0.5f;
- float32 xUR = m_pVolumeGeometry->pixelColToCenterX(_iCol) + m_pVolumeGeometry->getPixelLengthX() * 0.5f;
- float32 yUR = m_pVolumeGeometry->pixelRowToCenterY(_iRow) - m_pVolumeGeometry->getPixelLengthY() * 0.5f;
- float32 xLL = m_pVolumeGeometry->pixelColToCenterX(_iCol) - m_pVolumeGeometry->getPixelLengthX() * 0.5f;
- float32 yLL = m_pVolumeGeometry->pixelRowToCenterY(_iRow) + m_pVolumeGeometry->getPixelLengthY() * 0.5f;
- float32 xLR = m_pVolumeGeometry->pixelColToCenterX(_iCol) + m_pVolumeGeometry->getPixelLengthX() * 0.5f;
- float32 yLR = m_pVolumeGeometry->pixelRowToCenterY(_iRow) + m_pVolumeGeometry->getPixelLengthY() * 0.5f;
-
std::vector<SDetector2D> res;
- // loop projectors and detectors
- for (int iProjection = 0; iProjection < m_pProjectionGeometry->getProjectionAngleCount(); ++iProjection) {
-
- // get projection angle
- float32 theta = m_pProjectionGeometry->getProjectionAngle(iProjection);
- if (theta >= 7*PIdiv4) theta -= 2*PI;
- bool inverse = false;
- if (theta >= 3*PIdiv4) {
- theta -= PI;
- inverse = true;
- }
-
- // calculate distance from the center of the voxel to the ray though the origin
- float32 tUL = xUL * cos(theta) + yUL * sin(theta);
- float32 tUR = xUR * cos(theta) + yUR * sin(theta);
- float32 tLL = xLL * cos(theta) + yLL * sin(theta);
- float32 tLR = xLR * cos(theta) + yLR * sin(theta);
- if (inverse) {
- tUL *= -1.0f;
- tUR *= -1.0f;
- tLL *= -1.0f;
- tLR *= -1.0f;
- }
- float32 tMin = min(tUL, min(tUR, min(tLL,tLR)));
- float32 tMax = max(tUL, max(tUR, max(tLL,tLR)));
-
- // calculate the offset on the detectorarray (in indices)
- int dmin = (int)floor(m_pProjectionGeometry->detectorOffsetToIndexFloat(tMin));
- int dmax = (int)ceil(m_pProjectionGeometry->detectorOffsetToIndexFloat(tMax));
-
- // add affected detectors to the list
- for (int i = dmin; i <= dmax; ++i) {
- if (i >= 0 && i < m_pProjectionGeometry->getDetectorCount()) {
- SDetector2D det;
- det.m_iAngleIndex = iProjection;
- det.m_iDetectorIndex = i;
- det.m_iIndex = iProjection * getProjectionGeometry()->getDetectorCount() + i;
- res.push_back(det);
- }
- }
- }
-
- // return result vector
return res;
+ // float32 xUL = m_pVolumeGeometry->pixelColToCenterX(_iCol) - m_pVolumeGeometry->getPixelLengthX() * 0.5f;
+ // float32 yUL = m_pVolumeGeometry->pixelRowToCenterY(_iRow) - m_pVolumeGeometry->getPixelLengthY() * 0.5f;
+ // float32 xUR = m_pVolumeGeometry->pixelColToCenterX(_iCol) + m_pVolumeGeometry->getPixelLengthX() * 0.5f;
+ // float32 yUR = m_pVolumeGeometry->pixelRowToCenterY(_iRow) - m_pVolumeGeometry->getPixelLengthY() * 0.5f;
+ // float32 xLL = m_pVolumeGeometry->pixelColToCenterX(_iCol) - m_pVolumeGeometry->getPixelLengthX() * 0.5f;
+ // float32 yLL = m_pVolumeGeometry->pixelRowToCenterY(_iRow) + m_pVolumeGeometry->getPixelLengthY() * 0.5f;
+ // float32 xLR = m_pVolumeGeometry->pixelColToCenterX(_iCol) + m_pVolumeGeometry->getPixelLengthX() * 0.5f;
+ // float32 yLR = m_pVolumeGeometry->pixelRowToCenterY(_iRow) + m_pVolumeGeometry->getPixelLengthY() * 0.5f;
+
+ // std::vector<SDetector2D> res;
+ // // loop projectors and detectors
+ // for (int iProjection = 0; iProjection < m_pProjectionGeometry->getProjectionAngleCount(); ++iProjection) {
+
+ // // get projection angle
+ // float32 theta = m_pProjectionGeometry->getProjectionAngle(iProjection);
+ // if (theta >= 7*PIdiv4) theta -= 2*PI;
+ // bool inverse = false;
+ // if (theta >= 3*PIdiv4) {
+ // theta -= PI;
+ // inverse = true;
+ // }
+
+ // // calculate distance from the center of the voxel to the ray though the origin
+ // float32 tUL = xUL * cos(theta) + yUL * sin(theta);
+ // float32 tUR = xUR * cos(theta) + yUR * sin(theta);
+ // float32 tLL = xLL * cos(theta) + yLL * sin(theta);
+ // float32 tLR = xLR * cos(theta) + yLR * sin(theta);
+ // if (inverse) {
+ // tUL *= -1.0f;
+ // tUR *= -1.0f;
+ // tLL *= -1.0f;
+ // tLR *= -1.0f;
+ // }
+ // float32 tMin = min(tUL, min(tUR, min(tLL,tLR)));
+ // float32 tMax = max(tUL, max(tUR, max(tLL,tLR)));
+
+ // // calculate the offset on the detectorarray (in indices)
+ // int dmin = (int)floor(m_pProjectionGeometry->detectorOffsetToIndexFloat(tMin));
+ // int dmax = (int)ceil(m_pProjectionGeometry->detectorOffsetToIndexFloat(tMax));
+
+ // // add affected detectors to the list
+ // for (int i = dmin; i <= dmax; ++i) {
+ // if (i >= 0 && i < m_pProjectionGeometry->getDetectorCount()) {
+ // SDetector2D det;
+ // det.m_iAngleIndex = iProjection;
+ // det.m_iDetectorIndex = i;
+ // det.m_iIndex = iProjection * getProjectionGeometry()->getDetectorCount() + i;
+ // res.push_back(det);
+ // }
+ // }
+ // }
+
+ // // return result vector
+ // return res;
}
//----------------------------------------------------------------------------------------
diff --git a/src/ParallelBeamLinearKernelProjector2D.cpp b/src/ParallelBeamLinearKernelProjector2D.cpp
index e9034bf..98946a8 100644
--- a/src/ParallelBeamLinearKernelProjector2D.cpp
+++ b/src/ParallelBeamLinearKernelProjector2D.cpp
@@ -88,10 +88,10 @@ 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.");
+ ASTRA_CONFIG_CHECK(abs(m_pVolumeGeometry->getPixelLengthX() / m_pVolumeGeometry->getPixelLengthY()) - 1 < eps, "ParallelBeamLinearKernelProjector2D", "Pixel height must equal pixel width.");
// success
return true;
diff --git a/src/ParallelBeamStripKernelProjector2D.cpp b/src/ParallelBeamStripKernelProjector2D.cpp
index ab4e49e..5a0767b 100644
--- a/src/ParallelBeamStripKernelProjector2D.cpp
+++ b/src/ParallelBeamStripKernelProjector2D.cpp
@@ -87,9 +87,9 @@ 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.");
+ ASTRA_CONFIG_CHECK(abs(m_pVolumeGeometry->getPixelLengthX() / m_pVolumeGeometry->getPixelLengthY()) - 1 < eps, "ParallelBeamStripKernelProjector2D", "Pixel height must equal pixel width.");
// success
return true;
@@ -163,57 +163,57 @@ void CParallelBeamStripKernelProjector2D::computeSingleRayWeights(int _iProjecti
// Splat a single point
std::vector<SDetector2D> CParallelBeamStripKernelProjector2D::projectPoint(int _iRow, int _iCol)
{
- float32 xUL = m_pVolumeGeometry->pixelColToCenterX(_iCol) - m_pVolumeGeometry->getPixelLengthX() * 0.5f;
- float32 yUL = m_pVolumeGeometry->pixelRowToCenterY(_iRow) - m_pVolumeGeometry->getPixelLengthY() * 0.5f;
- float32 xUR = m_pVolumeGeometry->pixelColToCenterX(_iCol) + m_pVolumeGeometry->getPixelLengthX() * 0.5f;
- float32 yUR = m_pVolumeGeometry->pixelRowToCenterY(_iRow) - m_pVolumeGeometry->getPixelLengthY() * 0.5f;
- float32 xLL = m_pVolumeGeometry->pixelColToCenterX(_iCol) - m_pVolumeGeometry->getPixelLengthX() * 0.5f;
- float32 yLL = m_pVolumeGeometry->pixelRowToCenterY(_iRow) + m_pVolumeGeometry->getPixelLengthY() * 0.5f;
- float32 xLR = m_pVolumeGeometry->pixelColToCenterX(_iCol) + m_pVolumeGeometry->getPixelLengthX() * 0.5f;
- float32 yLR = m_pVolumeGeometry->pixelRowToCenterY(_iRow) + m_pVolumeGeometry->getPixelLengthY() * 0.5f;
+ // float32 xUL = m_pVolumeGeometry->pixelColToCenterX(_iCol) - m_pVolumeGeometry->getPixelLengthX() * 0.5f;
+ // float32 yUL = m_pVolumeGeometry->pixelRowToCenterY(_iRow) - m_pVolumeGeometry->getPixelLengthY() * 0.5f;
+ // float32 xUR = m_pVolumeGeometry->pixelColToCenterX(_iCol) + m_pVolumeGeometry->getPixelLengthX() * 0.5f;
+ // float32 yUR = m_pVolumeGeometry->pixelRowToCenterY(_iRow) - m_pVolumeGeometry->getPixelLengthY() * 0.5f;
+ // float32 xLL = m_pVolumeGeometry->pixelColToCenterX(_iCol) - m_pVolumeGeometry->getPixelLengthX() * 0.5f;
+ // float32 yLL = m_pVolumeGeometry->pixelRowToCenterY(_iRow) + m_pVolumeGeometry->getPixelLengthY() * 0.5f;
+ // float32 xLR = m_pVolumeGeometry->pixelColToCenterX(_iCol) + m_pVolumeGeometry->getPixelLengthX() * 0.5f;
+ // float32 yLR = m_pVolumeGeometry->pixelRowToCenterY(_iRow) + m_pVolumeGeometry->getPixelLengthY() * 0.5f;
std::vector<SDetector2D> res;
- // loop projectors and detectors
- for (int iProjection = 0; iProjection < m_pProjectionGeometry->getProjectionAngleCount(); ++iProjection) {
-
- // get projection angle
- float32 theta = m_pProjectionGeometry->getProjectionAngle(iProjection);
- if (theta >= 7*PIdiv4) theta -= 2*PI;
- bool inverse = false;
- if (theta >= 3*PIdiv4) {
- theta -= PI;
- inverse = true;
- }
-
- // calculate distance from the center of the voxel to the ray though the origin
- float32 tUL = xUL * cos(theta) + yUL * sin(theta);
- float32 tUR = xUR * cos(theta) + yUR * sin(theta);
- float32 tLL = xLL * cos(theta) + yLL * sin(theta);
- float32 tLR = xLR * cos(theta) + yLR * sin(theta);
- if (inverse) {
- tUL *= -1.0f;
- tUR *= -1.0f;
- tLL *= -1.0f;
- tLR *= -1.0f;
- }
- float32 tMin = min(tUL, min(tUR, min(tLL,tLR)));
- float32 tMax = max(tUL, max(tUR, max(tLL,tLR)));
-
- // calculate the offset on the detectorarray (in indices)
- int dmin = (int)floor(m_pProjectionGeometry->detectorOffsetToIndexFloat(tMin));
- int dmax = (int)ceil(m_pProjectionGeometry->detectorOffsetToIndexFloat(tMax));
-
- // add affected detectors to the list
- for (int i = dmin; i <= dmax; ++i) {
- if (i >= 0 && i < m_pProjectionGeometry->getDetectorCount()) {
- SDetector2D det;
- det.m_iAngleIndex = iProjection;
- det.m_iDetectorIndex = i;
- det.m_iIndex = iProjection * getProjectionGeometry()->getDetectorCount() + i;
- res.push_back(det);
- }
- }
- }
+ // // loop projectors and detectors
+ // for (int iProjection = 0; iProjection < m_pProjectionGeometry->getProjectionAngleCount(); ++iProjection) {
+
+ // // get projection angle
+ // float32 theta = m_pProjectionGeometry->getProjectionAngle(iProjection);
+ // if (theta >= 7*PIdiv4) theta -= 2*PI;
+ // bool inverse = false;
+ // if (theta >= 3*PIdiv4) {
+ // theta -= PI;
+ // inverse = true;
+ // }
+
+ // // calculate distance from the center of the voxel to the ray though the origin
+ // float32 tUL = xUL * cos(theta) + yUL * sin(theta);
+ // float32 tUR = xUR * cos(theta) + yUR * sin(theta);
+ // float32 tLL = xLL * cos(theta) + yLL * sin(theta);
+ // float32 tLR = xLR * cos(theta) + yLR * sin(theta);
+ // if (inverse) {
+ // tUL *= -1.0f;
+ // tUR *= -1.0f;
+ // tLL *= -1.0f;
+ // tLR *= -1.0f;
+ // }
+ // float32 tMin = min(tUL, min(tUR, min(tLL,tLR)));
+ // float32 tMax = max(tUL, max(tUR, max(tLL,tLR)));
+
+ // // calculate the offset on the detectorarray (in indices)
+ // int dmin = (int)floor(m_pProjectionGeometry->detectorOffsetToIndexFloat(tMin));
+ // int dmax = (int)ceil(m_pProjectionGeometry->detectorOffsetToIndexFloat(tMax));
+
+ // // add affected detectors to the list
+ // for (int i = dmin; i <= dmax; ++i) {
+ // if (i >= 0 && i < m_pProjectionGeometry->getDetectorCount()) {
+ // SDetector2D det;
+ // det.m_iAngleIndex = iProjection;
+ // det.m_iDetectorIndex = i;
+ // det.m_iIndex = iProjection * getProjectionGeometry()->getDetectorCount() + i;
+ // res.push_back(det);
+ // }
+ // }
+ // }
// return result vector
return res;
diff --git a/src/ParallelProjectionGeometry2D.cpp b/src/ParallelProjectionGeometry2D.cpp
index f73df50..3cc506a 100644
--- a/src/ParallelProjectionGeometry2D.cpp
+++ b/src/ParallelProjectionGeometry2D.cpp
@@ -27,6 +27,8 @@ along with the ASTRA Toolbox. If not, see <http://www.gnu.org/licenses/>.
#include "astra/ParallelProjectionGeometry2D.h"
+#include "astra/GeometryUtil2D.h"
+
#include <cstring>
using namespace std;
@@ -47,15 +49,13 @@ CParallelProjectionGeometry2D::CParallelProjectionGeometry2D() :
CParallelProjectionGeometry2D::CParallelProjectionGeometry2D(int _iProjectionAngleCount,
int _iDetectorCount,
float32 _fDetectorWidth,
- const float32* _pfProjectionAngles,
- const float32* _pfExtraDetectorOffsets)
+ const float32* _pfProjectionAngles)
{
_clear();
initialize(_iProjectionAngleCount,
_iDetectorCount,
_fDetectorWidth,
- _pfProjectionAngles,
- _pfExtraDetectorOffsets);
+ _pfProjectionAngles);
}
//----------------------------------------------------------------------------------------
@@ -65,8 +65,7 @@ CParallelProjectionGeometry2D::CParallelProjectionGeometry2D(const CParallelProj
initialize(_projGeom.m_iProjectionAngleCount,
_projGeom.m_iDetectorCount,
_projGeom.m_fDetectorWidth,
- _projGeom.m_pfProjectionAngles,
- _projGeom.m_pfExtraDetectorOffset);
+ _projGeom.m_pfProjectionAngles);
}
//----------------------------------------------------------------------------------------
@@ -115,14 +114,12 @@ bool CParallelProjectionGeometry2D::initialize(const Config& _cfg)
bool CParallelProjectionGeometry2D::initialize(int _iProjectionAngleCount,
int _iDetectorCount,
float32 _fDetectorWidth,
- const float32* _pfProjectionAngles,
- const float32* _pfExtraDetectorOffsets)
+ const float32* _pfProjectionAngles)
{
_initialize(_iProjectionAngleCount,
_iDetectorCount,
_fDetectorWidth,
- _pfProjectionAngles,
- _pfExtraDetectorOffsets);
+ _pfProjectionAngles);
// success
m_bInitialized = _check();
@@ -178,15 +175,10 @@ Config* CParallelProjectionGeometry2D::getConfiguration() const
cfg->self.addChildNode("DetectorCount", getDetectorCount());
cfg->self.addChildNode("DetectorWidth", getDetectorWidth());
cfg->self.addChildNode("ProjectionAngles", m_pfProjectionAngles, m_iProjectionAngleCount);
- if(m_pfExtraDetectorOffset!=NULL){
- XMLNode opt = cfg->self.addChildNode("Option");
- opt.addAttribute("key","ExtraDetectorOffset");
- opt.setContent(m_pfExtraDetectorOffset, m_iProjectionAngleCount);
- }
return cfg;
}
-//----------------------------------------------------------------------------------------
+//----------------------------------------------------------------------------------------
CVector3D CParallelProjectionGeometry2D::getProjectionDirection(int _iProjectionIndex, int _iDetectorIndex /* = 0 */)
{
CVector3D vOutput;
@@ -200,4 +192,18 @@ CVector3D CParallelProjectionGeometry2D::getProjectionDirection(int _iProjection
return vOutput;
}
+//----------------------------------------------------------------------------------------
+CParallelVecProjectionGeometry2D* CParallelProjectionGeometry2D::toVectorGeometry()
+{
+ SParProjection* vectors = genParProjections(m_iProjectionAngleCount,
+ m_iDetectorCount,
+ m_fDetectorWidth,
+ m_pfProjectionAngles, 0);
+ // TODO: ExtraOffsets?
+ CParallelVecProjectionGeometry2D* vecGeom = new CParallelVecProjectionGeometry2D();
+ vecGeom->initialize(m_iProjectionAngleCount, m_iDetectorCount, vectors);
+ delete[] vectors;
+ return vecGeom;
+}
+
} // end namespace astra
diff --git a/src/ParallelVecProjectionGeometry2D.cpp b/src/ParallelVecProjectionGeometry2D.cpp
new file mode 100644
index 0000000..1503076
--- /dev/null
+++ b/src/ParallelVecProjectionGeometry2D.cpp
@@ -0,0 +1,233 @@
+/*
+-----------------------------------------------------------------------
+Copyright: 2010-2015, iMinds-Vision Lab, University of Antwerp
+ 2014-2015, CWI, Amsterdam
+
+Contact: astra@uantwerpen.be
+Website: http://sf.net/projects/astra-toolbox
+
+This file is part of the ASTRA Toolbox.
+
+
+The ASTRA Toolbox is free software: you can redistribute it and/or modify
+it under the terms of the GNU General Public License as published by
+the Free Software Foundation, either version 3 of the License, or
+(at your option) any later version.
+
+The ASTRA Toolbox is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+GNU General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with the ASTRA Toolbox. If not, see <http://www.gnu.org/licenses/>.
+
+-----------------------------------------------------------------------
+$Id$
+*/
+
+#include "astra/ParallelVecProjectionGeometry2D.h"
+
+#include <cstring>
+#include <sstream>
+#include <boost/lexical_cast.hpp>
+
+using namespace std;
+
+namespace astra
+{
+
+//----------------------------------------------------------------------------------------
+// Default constructor. Sets all variables to zero.
+CParallelVecProjectionGeometry2D::CParallelVecProjectionGeometry2D()
+{
+ _clear();
+ m_pProjectionAngles = 0;
+}
+
+//----------------------------------------------------------------------------------------
+// Constructor.
+CParallelVecProjectionGeometry2D::CParallelVecProjectionGeometry2D(int _iProjectionAngleCount,
+ int _iDetectorCount,
+ const SParProjection* _pProjectionAngles)
+{
+ this->initialize(_iProjectionAngleCount,
+ _iDetectorCount,
+ _pProjectionAngles);
+}
+
+//----------------------------------------------------------------------------------------
+// Copy Constructor
+CParallelVecProjectionGeometry2D::CParallelVecProjectionGeometry2D(const CParallelVecProjectionGeometry2D& _projGeom)
+{
+ _clear();
+ this->initialize(_projGeom.m_iProjectionAngleCount,
+ _projGeom.m_iDetectorCount,
+ _projGeom.m_pProjectionAngles);
+}
+
+//----------------------------------------------------------------------------------------
+// Destructor.
+CParallelVecProjectionGeometry2D::~CParallelVecProjectionGeometry2D()
+{
+ // TODO
+ delete[] m_pProjectionAngles;
+}
+
+
+//----------------------------------------------------------------------------------------
+// Initialization.
+bool CParallelVecProjectionGeometry2D::initialize(int _iProjectionAngleCount,
+ int _iDetectorCount,
+ const SParProjection* _pProjectionAngles)
+{
+ m_iProjectionAngleCount = _iProjectionAngleCount;
+ m_iDetectorCount = _iDetectorCount;
+ m_pProjectionAngles = new SParProjection[m_iProjectionAngleCount];
+ for (int i = 0; i < m_iProjectionAngleCount; ++i)
+ m_pProjectionAngles[i] = _pProjectionAngles[i];
+
+ // TODO: check?
+
+ // success
+ m_bInitialized = _check();
+ return m_bInitialized;
+}
+
+//----------------------------------------------------------------------------------------
+// Initialization with a Config object
+bool CParallelVecProjectionGeometry2D::initialize(const Config& _cfg)
+{
+ ASTRA_ASSERT(_cfg.self);
+ ConfigStackCheck<CProjectionGeometry2D> CC("ParallelVecProjectionGeometry2D", this, _cfg);
+
+ // TODO: Fix up class hierarchy... this class doesn't fit very well.
+ // initialization of parent class
+ //CProjectionGeometry2D::initialize(_cfg);
+
+ // Required: DetectorCount
+ XMLNode node = _cfg.self.getSingleNode("DetectorCount");
+ ASTRA_CONFIG_CHECK(node, "ParallelVecProjectionGeometry2D", "No DetectorRowCount tag specified.");
+ m_iDetectorCount = boost::lexical_cast<int>(node.getContent());
+ CC.markNodeParsed("DetectorCount");
+
+ // Required: Vectors
+ node = _cfg.self.getSingleNode("Vectors");
+ ASTRA_CONFIG_CHECK(node, "ParallelVecProjectionGeometry2D", "No Vectors tag specified.");
+ vector<float32> data = node.getContentNumericalArray();
+ CC.markNodeParsed("Vectors");
+ ASTRA_CONFIG_CHECK(data.size() % 6 == 0, "ParallelVecProjectionGeometry2D", "Vectors doesn't consist of 6-tuples.");
+ m_iProjectionAngleCount = data.size() / 6;
+ m_pProjectionAngles = new SParProjection[m_iProjectionAngleCount];
+
+ for (int i = 0; i < m_iProjectionAngleCount; ++i) {
+ SParProjection& p = m_pProjectionAngles[i];
+ p.fRayX = data[6*i + 0];
+ p.fRayY = data[6*i + 1];
+ p.fDetUX = data[6*i + 4];
+ p.fDetUY = data[6*i + 5];
+
+ // The backend code currently expects the corner of the detector, while
+ // the matlab interface supplies the center
+ p.fDetSX = data[6*i + 2] - 0.5f * m_iDetectorCount * p.fDetUX;
+ p.fDetSY = data[6*i + 3] - 0.5f * m_iDetectorCount * p.fDetUY;
+ }
+
+ // success
+ m_bInitialized = _check();
+ return m_bInitialized;
+}
+
+//----------------------------------------------------------------------------------------
+// Clone
+CProjectionGeometry2D* CParallelVecProjectionGeometry2D::clone()
+{
+ return new CParallelVecProjectionGeometry2D(*this);
+}
+
+//----------------------------------------------------------------------------------------
+// is equal
+bool CParallelVecProjectionGeometry2D::isEqual(CProjectionGeometry2D* _pGeom2) const
+{
+ if (_pGeom2 == NULL) return false;
+
+ // try to cast argument to CParallelVecProjectionGeometry2D
+ CParallelVecProjectionGeometry2D* pGeom2 = dynamic_cast<CParallelVecProjectionGeometry2D*>(_pGeom2);
+ if (pGeom2 == NULL) return false;
+
+ // both objects must be initialized
+ if (!m_bInitialized || !pGeom2->m_bInitialized) return false;
+
+ // check all values
+ if (m_iProjectionAngleCount != pGeom2->m_iProjectionAngleCount) return false;
+ if (m_iDetectorCount != pGeom2->m_iDetectorCount) return false;
+
+ for (int i = 0; i < m_iProjectionAngleCount; ++i) {
+ if (memcmp(&m_pProjectionAngles[i], &pGeom2->m_pProjectionAngles[i], sizeof(m_pProjectionAngles[i])) != 0) return false;
+ }
+
+ return true;
+}
+
+//----------------------------------------------------------------------------------------
+// Is of type
+bool CParallelVecProjectionGeometry2D::isOfType(const std::string& _sType)
+{
+ return (_sType == "parallel_vec");
+}
+
+//----------------------------------------------------------------------------------------
+
+CVector3D CParallelVecProjectionGeometry2D::getProjectionDirection(int _iProjectionIndex, int _iDetectorIndex /* = 0 */)
+{
+ CVector3D vOutput(0.0f, 0.0f, 0.0f);
+
+ // not implemented
+ ASTRA_ASSERT(false);
+
+ return vOutput;
+}
+
+//----------------------------------------------------------------------------------------
+
+void CParallelVecProjectionGeometry2D::getRayParams(int _iRow, int _iColumn, float32& _fT, float32& _fTheta) const
+{
+ // not implemented
+ ASTRA_ASSERT(false);
+}
+
+//----------------------------------------------------------------------------------------
+
+bool CParallelVecProjectionGeometry2D::_check()
+{
+ // TODO
+ return true;
+}
+
+
+//----------------------------------------------------------------------------------------
+// Get the configuration object
+Config* CParallelVecProjectionGeometry2D::getConfiguration() const
+{
+ Config* cfg = new Config();
+ cfg->initialize("ProjectionGeometry2D");
+ cfg->self.addAttribute("type", "parallel_vec");
+ cfg->self.addChildNode("DetectorCount", getDetectorCount());
+ std::string vectors = "";
+ for (int i = 0; i < m_iProjectionAngleCount; ++i) {
+ SParProjection& p = m_pProjectionAngles[i];
+ vectors += boost::lexical_cast<string>(p.fRayX) + ",";
+ vectors += boost::lexical_cast<string>(p.fRayY) + ",";
+ vectors += boost::lexical_cast<string>(p.fDetSX + 0.5f * m_iDetectorCount * p.fDetUX) + ",";
+ vectors += boost::lexical_cast<string>(p.fDetSY + 0.5f * m_iDetectorCount * p.fDetUY) + ",";
+ vectors += boost::lexical_cast<string>(p.fDetUX) + ",";
+ vectors += boost::lexical_cast<string>(p.fDetUY);
+ if (i < m_iProjectionAngleCount-1) vectors += ';';
+ }
+ cfg->self.addChildNode("Vectors", vectors);
+ return cfg;
+}
+//----------------------------------------------------------------------------------------
+
+
+} // namespace astra
diff --git a/src/ProjectionGeometry2D.cpp b/src/ProjectionGeometry2D.cpp
index e9f08ec..54605a7 100644
--- a/src/ProjectionGeometry2D.cpp
+++ b/src/ProjectionGeometry2D.cpp
@@ -44,11 +44,10 @@ CProjectionGeometry2D::CProjectionGeometry2D() : configCheckData(0)
CProjectionGeometry2D::CProjectionGeometry2D(int _iAngleCount,
int _iDetectorCount,
float32 _fDetectorWidth,
- const float32* _pfProjectionAngles,
- const float32* _pfExtraDetectorOffsets) : configCheckData(0)
+ const float32* _pfProjectionAngles) : configCheckData(0)
{
_clear();
- _initialize(_iAngleCount, _iDetectorCount, _fDetectorWidth, _pfProjectionAngles,_pfExtraDetectorOffsets);
+ _initialize(_iAngleCount, _iDetectorCount, _fDetectorWidth, _pfProjectionAngles);
}
//----------------------------------------------------------------------------------------
@@ -69,7 +68,6 @@ void CProjectionGeometry2D::_clear()
m_iDetectorCount = 0;
m_fDetectorWidth = 0.0f;
m_pfProjectionAngles = NULL;
- m_pfExtraDetectorOffset = NULL;
m_bInitialized = false;
}
@@ -82,10 +80,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;
}
@@ -144,19 +140,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.");
@@ -171,8 +154,7 @@ bool CProjectionGeometry2D::initialize(const Config& _cfg)
bool CProjectionGeometry2D::_initialize(int _iProjectionAngleCount,
int _iDetectorCount,
float32 _fDetectorWidth,
- const float32* _pfProjectionAngles,
- const float32* _pfExtraDetectorOffsets)
+ const float32* _pfProjectionAngles)
{
if (m_bInitialized) {
clear();
@@ -183,10 +165,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 78412e6..ced5b84 100644
--- a/src/Projector2D.cpp
+++ b/src/Projector2D.cpp
@@ -27,6 +27,7 @@ along with the ASTRA Toolbox. If not, see <http://www.gnu.org/licenses/>.
#include "astra/Projector2D.h"
+#include "astra/ParallelVecProjectionGeometry2D.h"
#include "astra/FanFlatProjectionGeometry2D.h"
#include "astra/FanFlatVecProjectionGeometry2D.h"
#include "astra/SparseMatrixProjectionGeometry2D.h"
@@ -130,6 +131,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));
diff --git a/src/VolumeGeometry2D.cpp b/src/VolumeGeometry2D.cpp
index dd056ae..75d0dfc 100644
--- a/src/VolumeGeometry2D.cpp
+++ b/src/VolumeGeometry2D.cpp
@@ -41,16 +41,16 @@ bool CVolumeGeometry2D::_check()
ASTRA_CONFIG_CHECK(m_fWindowMinX < m_fWindowMaxX, "VolumeGeometry2D", "WindowMinX should be lower than WindowMaxX.");
ASTRA_CONFIG_CHECK(m_fWindowMinY < m_fWindowMaxY, "VolumeGeometry2D", "WindowMinY should be lower than WindowMaxY.");
- ASTRA_CONFIG_CHECK(m_iGridTotCount == (m_iGridColCount * m_iGridRowCount), "VolumeGeometry2D", "Internal configuration error.");
- ASTRA_CONFIG_CHECK(m_fWindowLengthX == (m_fWindowMaxX - m_fWindowMinX), "VolumeGeometry2D", "Internal configuration error.");
- ASTRA_CONFIG_CHECK(m_fWindowLengthY == (m_fWindowMaxY - m_fWindowMinY), "VolumeGeometry2D", "Internal configuration error.");
- ASTRA_CONFIG_CHECK(m_fWindowArea == (m_fWindowLengthX * m_fWindowLengthY), "VolumeGeometry2D", "Internal configuration error.");
- ASTRA_CONFIG_CHECK(m_fPixelLengthX == (m_fWindowLengthX / (float32)m_iGridColCount), "VolumeGeometry2D", "Internal configuration error.");
- ASTRA_CONFIG_CHECK(m_fPixelLengthY == (m_fWindowLengthY / (float32)m_iGridRowCount), "VolumeGeometry2D", "Internal configuration error.");
-
- ASTRA_CONFIG_CHECK(m_fPixelArea == (m_fPixelLengthX * m_fPixelLengthY), "VolumeGeometry2D", "Internal configuration error.");
- ASTRA_CONFIG_CHECK(fabsf(m_fDivPixelLengthX * m_fPixelLengthX - 1.0f) < eps, "VolumeGeometry2D", "Internal configuration error.");
- ASTRA_CONFIG_CHECK(fabsf(m_fDivPixelLengthY * m_fPixelLengthY - 1.0f) < eps, "VolumeGeometry2D", "Internal configuration error.");
+ ASTRA_CONFIG_CHECK(fabsf(m_iGridTotCount - (m_iGridColCount * m_iGridRowCount)) < eps, "VolumeGeometry2D", "Internal configuration error (m_iGridTotCount).");
+ ASTRA_CONFIG_CHECK(fabsf(m_fWindowLengthX - (m_fWindowMaxX - m_fWindowMinX)) < eps, "VolumeGeometry2D", "Internal configuration error (m_fWindowLengthX).");
+ ASTRA_CONFIG_CHECK(fabsf(m_fWindowLengthY - (m_fWindowMaxY - m_fWindowMinY)) < eps, "VolumeGeometry2D", "Internal configuration error (m_fWindowLengthY).");
+ ASTRA_CONFIG_CHECK(fabsf(m_fWindowArea - (m_fWindowLengthX * m_fWindowLengthY)) < eps, "VolumeGeometry2D", "Internal configuration error (m_fWindowArea).");
+ ASTRA_CONFIG_CHECK(fabsf(m_fPixelLengthX - (m_fWindowLengthX / (float32)m_iGridColCount)) < eps, "VolumeGeometry2D", "Internal configuration error (m_fPixelLengthX).");
+ ASTRA_CONFIG_CHECK(fabsf(m_fPixelLengthY - (m_fWindowLengthY / (float32)m_iGridRowCount)) < eps, "VolumeGeometry2D", "Internal configuration error (m_fPixelLengthY).");
+
+ ASTRA_CONFIG_CHECK(fabsf(m_fPixelArea - (m_fPixelLengthX * m_fPixelLengthY)) < eps, "VolumeGeometry2D", "Internal configuration error (m_fPixelArea).");
+ ASTRA_CONFIG_CHECK(fabsf(m_fDivPixelLengthX * m_fPixelLengthX - 1.0f) < eps, "VolumeGeometry2D", "Internal configuration error (m_fDivPixelLengthX).");
+ ASTRA_CONFIG_CHECK(fabsf(m_fDivPixelLengthY * m_fPixelLengthY - 1.0f) < eps, "VolumeGeometry2D", "Internal configuration error (m_fDivPixelLengthY).");
return true;
}
diff --git a/tests/python/test_line2d.py b/tests/python/test_line2d.py
new file mode 100644
index 0000000..de68033
--- /dev/null
+++ b/tests/python/test_line2d.py
@@ -0,0 +1,310 @@
+import numpy as np
+import unittest
+import astra
+import math
+import pylab
+
+# return length of intersection of the line through points src = (x,y)
+# and det (x,y), and the rectangle defined by xmin, ymin, xmax, ymax
+#
+# TODO: Generalize from 2D to n-dimensional
+def intersect_line_rectangle(src, det, xmin, xmax, ymin, ymax):
+ EPS = 1e-5
+
+ if np.abs(src[0] - det[0]) < EPS:
+ if src[0] >= xmin and src[0] < xmax:
+ return ymax - ymin
+ else:
+ return 0.0
+ if np.abs(src[1] - det[1]) < EPS:
+ if src[1] >= ymin and src[1] < ymax:
+ return xmax - xmin
+ else:
+ return 0.0
+
+ n = np.sqrt((det[0] - src[0]) ** 2 + (det[1] - src[1]) ** 2)
+
+ check = [ (-(xmin - src[0]), -(det[0] - src[0]) / n ),
+ (xmax - src[0], (det[0] - src[0]) / n ),
+ (-(ymin - src[1]), -(det[1] - src[1]) / n ),
+ (ymax - src[1], (det[1] - src[1]) / n ) ]
+
+ pre = [ -np.Inf ]
+ post = [ np.Inf ]
+
+ for p, q in check:
+ r = p / (1.0 * q)
+ if q > 0:
+ post.append(r) # exiting half-plane
+ else:
+ pre.append(r) # entering half-plane
+
+ end_r = np.min(post)
+ start_r = np.max(pre)
+
+ if end_r > start_r:
+ return end_r - start_r
+ else:
+ return 0.0
+
+def intersect_line_rectangle_feather(src, det, xmin, xmax, ymin, ymax, feather):
+ return intersect_line_rectangle(src, det,
+ xmin-feather, xmax+feather,
+ ymin-feather, ymax+feather)
+
+def intersect_line_rectangle_interval(src, det, xmin, xmax, ymin, ymax, f):
+ a = intersect_line_rectangle_feather(src, det, xmin, xmax, ymin, ymax, -f)
+ b = intersect_line_rectangle(src, det, xmin, xmax, ymin, ymax)
+ c = intersect_line_rectangle_feather(src, det, xmin, xmax, ymin, ymax, f)
+ return (a,b,c)
+
+def gen_lines_fanflat(proj_geom):
+ angles = proj_geom['ProjectionAngles']
+ for theta in angles:
+ #theta = -theta
+ src = ( math.sin(theta) * proj_geom['DistanceOriginSource'],
+ -math.cos(theta) * proj_geom['DistanceOriginSource'] )
+ detc= (-math.sin(theta) * proj_geom['DistanceOriginDetector'],
+ math.cos(theta) * proj_geom['DistanceOriginDetector'] )
+ detu= ( math.cos(theta) * proj_geom['DetectorWidth'],
+ math.sin(theta) * proj_geom['DetectorWidth'] )
+
+ src = np.array(src, dtype=np.float64)
+ detc= np.array(detc, dtype=np.float64)
+ detu= np.array(detu, dtype=np.float64)
+
+ detb= detc + (0.5 - 0.5*proj_geom['DetectorCount']) * detu
+
+ for i in range(proj_geom['DetectorCount']):
+ yield (src, detb + i * detu)
+
+def gen_lines_fanflat_vec(proj_geom):
+ v = proj_geom['Vectors']
+ for i in range(v.shape[0]):
+ src = v[i,0:2]
+ detc = v[i,2:4]
+ detu = v[i,4:6]
+
+ detb = detc + (0.5 - 0.5*proj_geom['DetectorCount']) * detu
+ for i in range(proj_geom['DetectorCount']):
+ yield (src, detb + i * detu)
+
+def gen_lines_parallel(proj_geom):
+ angles = proj_geom['ProjectionAngles']
+ for theta in angles:
+ ray = ( math.sin(theta),
+ -math.cos(theta) )
+ detc= (0, 0 )
+ detu= ( math.cos(theta) * proj_geom['DetectorWidth'],
+ math.sin(theta) * proj_geom['DetectorWidth'] )
+
+ ray = np.array(ray, dtype=np.float64)
+ detc= np.array(detc, dtype=np.float64)
+ detu= np.array(detu, dtype=np.float64)
+
+
+ detb= detc + (0.5 - 0.5*proj_geom['DetectorCount']) * detu
+
+ for i in range(proj_geom['DetectorCount']):
+ yield (detb + i * detu - ray, detb + i * detu)
+
+def gen_lines_parallel_vec(proj_geom):
+ v = proj_geom['Vectors']
+ for i in range(v.shape[0]):
+ ray = v[i,0:2]
+ detc = v[i,2:4]
+ detu = v[i,4:6]
+
+ detb = detc + (0.5 - 0.5*proj_geom['DetectorCount']) * detu
+
+ for i in range(proj_geom['DetectorCount']):
+ yield (detb + i * detu - ray, detb + i * detu)
+
+
+def gen_lines(proj_geom):
+ g = { 'fanflat': gen_lines_fanflat,
+ 'fanflat_vec': gen_lines_fanflat_vec,
+ 'parallel': gen_lines_parallel,
+ 'parallel_vec': gen_lines_parallel_vec }
+ for l in g[proj_geom['type']](proj_geom):
+ yield l
+
+range2d = ( 8, 64 )
+
+
+def gen_random_geometry_fanflat():
+ pg = astra.create_proj_geom('fanflat', 0.6 + 0.8 * np.random.random(), np.random.randint(*range2d), np.linspace(0, 2*np.pi, np.random.randint(*range2d), endpoint=False), 256 * (0.5 + np.random.random()), 256 * np.random.random())
+ return pg
+
+def gen_random_geometry_parallel():
+ pg = astra.create_proj_geom('parallel', 0.8 + 0.4 * np.random.random(), np.random.randint(*range2d), np.linspace(0, 2*np.pi, np.random.randint(*range2d), endpoint=False))
+ return pg
+
+def gen_random_geometry_fanflat_vec():
+ Vectors = np.zeros([16,6])
+ # We assume constant detector width in these tests
+ w = 0.6 + 0.8 * np.random.random()
+ for i in range(Vectors.shape[0]):
+ angle1 = 2*np.pi*np.random.random()
+ angle2 = angle1 + 0.5 * np.random.random()
+ dist1 = 256 * (0.5 + np.random.random())
+ detc = 10 * np.random.random(size=2)
+ detu = [ math.cos(angle1) * w, math.sin(angle1) * w ]
+ src = [ math.sin(angle2) * dist1, -math.cos(angle2) * dist1 ]
+ Vectors[i, :] = [ src[0], src[1], detc[0], detc[1], detu[0], detu[1] ]
+ pg = astra.create_proj_geom('fanflat_vec', np.random.randint(*range2d), Vectors)
+
+ # TODO: Randomize more
+ pg = astra.create_proj_geom('fanflat_vec', np.random.randint(*range2d), Vectors)
+ return pg
+
+def gen_random_geometry_parallel_vec():
+ Vectors = np.zeros([16,6])
+ # We assume constant detector width in these tests
+ w = 0.6 + 0.8 * np.random.random()
+ for i in range(Vectors.shape[0]):
+ l = 0.6 + 0.8 * np.random.random()
+ angle1 = 2*np.pi*np.random.random()
+ angle2 = angle1 + 0.5 * np.random.random()
+ detc = 10 * np.random.random(size=2)
+ detu = [ math.cos(angle1) * w, math.sin(angle1) * w ]
+ ray = [ math.sin(angle2) * l, -math.cos(angle2) * l ]
+ Vectors[i, :] = [ ray[0], ray[1], detc[0], detc[1], detu[0], detu[1] ]
+ pg = astra.create_proj_geom('parallel_vec', np.random.randint(*range2d), Vectors)
+ return pg
+
+
+
+
+nloops = 50
+seed = 123
+
+class TestLineKernel(unittest.TestCase):
+ def single_test(self, type):
+ shape = np.random.randint(*range2d, size=2)
+ # these rectangles are biased, but that shouldn't matter
+ rect_min = [ np.random.randint(0, a) for a in shape ]
+ rect_max = [ np.random.randint(rect_min[i]+1, shape[i]+1) for i in range(len(shape))]
+ if True:
+ #pixsize = 0.5 + np.random.random(size=2)
+ pixsize = np.array([0.5, 0.5]) + np.random.random()
+ origin = 10 * np.random.random(size=2)
+ else:
+ pixsize = (1.,1.)
+ origin = (0.,0.)
+ vg = astra.create_vol_geom(shape[1], shape[0],
+ origin[0] - 0.5 * shape[0] * pixsize[0],
+ origin[0] + 0.5 * shape[0] * pixsize[0],
+ origin[1] - 0.5 * shape[1] * pixsize[1],
+ origin[1] + 0.5 * shape[1] * pixsize[1])
+ #print(vg)
+
+ if type == 'parallel':
+ pg = gen_random_geometry_parallel()
+ projector_id = astra.create_projector('line', pg, vg)
+ elif type == 'parallel_vec':
+ pg = gen_random_geometry_parallel_vec()
+ projector_id = astra.create_projector('line', pg, vg)
+ elif type == 'fanflat':
+ pg = gen_random_geometry_fanflat()
+ projector_id = astra.create_projector('line_fanflat', pg, vg)
+ elif type == 'fanflat_vec':
+ pg = gen_random_geometry_fanflat_vec()
+ projector_id = astra.create_projector('line_fanflat', pg, vg)
+
+
+ data = np.zeros((shape[1], shape[0]), dtype=np.float32)
+ data[rect_min[1]:rect_max[1],rect_min[0]:rect_max[0]] = 1
+
+ sinogram_id, sinogram = astra.create_sino(data, projector_id)
+
+ #print(pg)
+ #print(vg)
+
+ astra.data2d.delete(sinogram_id)
+
+ astra.projector.delete(projector_id)
+
+ a = np.zeros(np.prod(astra.functions.geom_size(pg)), dtype=np.float32)
+ b = np.zeros(np.prod(astra.functions.geom_size(pg)), dtype=np.float32)
+ c = np.zeros(np.prod(astra.functions.geom_size(pg)), dtype=np.float32)
+
+ i = 0
+ #print( origin[0] + (-0.5 * shape[0] + rect_min[0]) * pixsize[0], origin[0] + (-0.5 * shape[0] + rect_max[0]) * pixsize[0], origin[1] + (-0.5 * shape[1] + rect_min[1]) * pixsize[1], origin[1] + (-0.5 * shape[1] + rect_max[1]) * pixsize[1])
+ for src, det in gen_lines(pg):
+ #print(src,det)
+
+ # NB: Flipped y-axis here, since that is how astra interprets 2D volumes
+ # We compute line intersections with slightly bigger (cw) and
+ # smaller (aw) rectangles, and see if the kernel falls
+ # between these two values.
+ (aw,bw,cw) = intersect_line_rectangle_interval(src, det,
+ origin[0] + (-0.5 * shape[0] + rect_min[0]) * pixsize[0],
+ origin[0] + (-0.5 * shape[0] + rect_max[0]) * pixsize[0],
+ origin[1] + (+0.5 * shape[1] - rect_max[1]) * pixsize[1],
+ origin[1] + (+0.5 * shape[1] - rect_min[1]) * pixsize[1],
+ 1e-3)
+ a[i] = aw
+ b[i] = bw
+ c[i] = cw
+ i += 1
+ # Add weight for pixel / voxel size
+ try:
+ detweight = pg['DetectorWidth']
+ except KeyError:
+ detweight = np.sqrt(pg['Vectors'][0,4]*pg['Vectors'][0,4] + pg['Vectors'][0,5]*pg['Vectors'][0,5] )
+ a *= detweight
+ b *= detweight
+ c *= detweight
+ a = a.reshape(astra.functions.geom_size(pg))
+ b = b.reshape(astra.functions.geom_size(pg))
+ c = c.reshape(astra.functions.geom_size(pg))
+
+ # Check if sinogram lies between a and c
+ y = np.min(sinogram-a)
+ z = np.min(c-sinogram)
+ x = np.max(np.abs(sinogram-b)) # ideally this is small, but can be large
+ # due to discontinuities in line kernel
+ self.assertFalse(z < 0 or y < 0)
+ if z < 0 or y < 0:
+ print(y,z,x)
+ pylab.gray()
+ pylab.imshow(data)
+ pylab.figure()
+ pylab.imshow(sinogram)
+ pylab.figure()
+ pylab.imshow(b)
+ pylab.figure()
+ pylab.imshow(a)
+ pylab.figure()
+ pylab.imshow(c)
+ pylab.figure()
+ pylab.imshow(sinogram-a)
+ pylab.figure()
+ pylab.imshow(c-sinogram)
+ pylab.show()
+
+ def test_par(self):
+ np.random.seed(seed)
+ for _ in range(nloops):
+ self.single_test('parallel')
+ def test_fan(self):
+ np.random.seed(seed)
+ for _ in range(nloops):
+ self.single_test('fanflat')
+ def test_parvec(self):
+ np.random.seed(seed)
+ for _ in range(nloops):
+ self.single_test('parallel_vec')
+ def test_fanvec(self):
+ np.random.seed(seed)
+ for _ in range(nloops):
+ self.single_test('fanflat_vec')
+
+
+
+
+if __name__ == '__main__':
+ unittest.main()
+
+#print(intersect_line_rectangle((0.,-256.),(-27.,0.),11.6368454385 20.173128227 3.18989047649 5.62882841606)
diff --git a/tests/test_ParallelBeamLineKernelProjector2D.cpp b/tests/test_ParallelBeamLineKernelProjector2D.cpp
index 58d511e..575799c 100644
--- a/tests/test_ParallelBeamLineKernelProjector2D.cpp
+++ b/tests/test_ParallelBeamLineKernelProjector2D.cpp
@@ -74,7 +74,7 @@ BOOST_FIXTURE_TEST_CASE( testParallelBeamLineKernelProjector2D_Rectangle, TestPa
for (int i = 0; i < iCount; ++i)
fWeight += pPix[i].m_fWeight;
- BOOST_CHECK_SMALL(fWeight - 7.13037f, 0.00001f);
+ BOOST_CHECK_SMALL(fWeight - 7.13037f, 0.00001f); // 6 / sin(1)
delete[] pPix;
}
diff --git a/tests/test_ParallelBeamLinearKernelProjector2D.cpp b/tests/test_ParallelBeamLinearKernelProjector2D.cpp
index a34c82c..c62f670 100644
--- a/tests/test_ParallelBeamLinearKernelProjector2D.cpp
+++ b/tests/test_ParallelBeamLinearKernelProjector2D.cpp
@@ -85,10 +85,10 @@ float32 compute_linear_kernel(const astra::CProjectionGeometry2D& projgeom, cons
if (fabs(cos(fAngle)) > fabs(sin(fAngle))) {
fDetStep = volgeom.getPixelLengthY() * fabs(cos(fAngle));
- fWeight = volgeom.getPixelLengthX() * 1.0f / fabs(cos(fAngle));
+ fWeight = projgeom.getDetectorWidth() * volgeom.getPixelLengthX() * 1.0f / fabs(cos(fAngle));
} else {
fDetStep = volgeom.getPixelLengthX() * fabs(sin(fAngle));
- fWeight = volgeom.getPixelLengthY() * 1.0f / fabs(sin(fAngle));
+ fWeight = projgeom.getDetectorWidth() * volgeom.getPixelLengthY() * 1.0f / fabs(sin(fAngle));
}
// printf("step: %f\n weight: %f\n", fDetStep, fWeight);