diff options
73 files changed, 3379 insertions, 2821 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 c83e9f0..d7d6488 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 \ @@ -214,6 +216,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 dc3bb05..c1d932c 100644 --- a/cuda/2d/algo.h +++ b/cuda/2d/algo.h @@ -30,6 +30,17 @@ along with the ASTRA Toolbox. If not, see <http://www.gnu.org/licenses/>.  #include "util.h" +namespace astra { + +class CParallelProjectionGeometry2D; +class CParallelVecProjectionGeometry2D; +class CFanFlatProjectionGeometry2D; +class CFanFlatVecProjectionGeometry2D; +class CVolumeGeometry2D; +class CProjectionGeometry2D; + +} +  namespace astraCUDA {  class _AstraExport ReconAlgo { @@ -39,11 +50,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; } @@ -122,9 +132,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 2ff9230..279b57d 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]; +	return true; +} -		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; -		} -		if (offCenter) { -			float dx = (pVolGeom->getWindowMinX() + pVolGeom->getWindowMaxX()) / 2; -			float dy = (pVolGeom->getWindowMinY() + pVolGeom->getWindowMaxY()) / 2; -			// CHECKME: Is d in pixels or in units? +bool convertAstraGeometry(const CVolumeGeometry2D* pVolGeom, +                          const CParallelProjectionGeometry2D* pProjGeom, +                          SParProjection*& pProjs, +                          float& fOutputScale) +{ +	assert(pVolGeom); +	assert(pProjGeom); +	assert(pProjGeom->getProjectionAngles()); -			for (int i = 0; i < nth; ++i) { -				float d = dx * cos(angles[i]) + dy * sin(angles[i]); -				offset[i] += d; -			} -		} +	int nth = pProjGeom->getProjectionAngleCount(); -		// CHECKME: Order of scaling and translation +	pProjs = genParProjections(nth, +	                           pProjGeom->getDetectorCount(), +	                           pProjGeom->getDetectorWidth(), +	                           pProjGeom->getProjectionAngles(), 0); -		// Scale volume pixels to 1x1 -		for (int i = 0; i < nth; ++i) { -			//offset[i] /= pVolGeom->getPixelLengthX(); -			//offset[i] *= detSize; -		} +	bool ok; +	fOutputScale = 1.0f; +	ok = convertAstraGeometry_internal(pVolGeom, nth, pProjs, fOutputScale); -		detectorOffsets = offset; -	} else { -		detectorOffsets = 0; +	if (!ok) { +		delete[] pProjs; +		pProjs = 0;  	} -	outputScale = pVolGeom->getPixelLengthX(); -	outputScale *= outputScale; - -	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(); @@ -986,6 +442,52 @@ bool convertAstraGeometry(const CVolumeGeometry2D* pVolGeom,  } +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 78d00e3..a20b830 100644 --- a/cuda/2d/astra.h +++ b/cuda/2d/astra.h @@ -28,7 +28,6 @@ along with the ASTRA Toolbox. If not, see <http://www.gnu.org/licenses/>.  #ifndef _CUDA_ASTRA_H  #define _CUDA_ASTRA_H -#include "fft.h"  #include "fbp_filters.h"  #include "dims.h"  #include "algo.h" @@ -42,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: @@ -198,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, @@ -212,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, @@ -225,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);  }  #endif 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..04cac1e --- /dev/null +++ b/cuda/2d/fbp.cu @@ -0,0 +1,346 @@ +/* +----------------------------------------------------------------------- +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" + +// 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 057843e..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,15 +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); -	 -	//debug -	static void testGenFilter(E_FBPFILTER _eFilter, float _fD, int _iProjectionCount, cufftComplex * _pFilter, int _iFFTRealDetectorCount, int _iFFTFourierDetectorCount); -	static int getGPUCount(); -  	/** Get a description of the class.  	 *  	 * @return description string @@ -83,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..c1e1e94 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; - -			// get values -			if (pProjectionGeometry) { -				t = -pProjectionGeometry->indexToDetectorOffset(iDetector); -				alpha = atan(t / pProjectionGeometry->getSourceDetectorDistance()); -				t = sin(alpha) * pProjectionGeometry->getOriginSourceDistance(); -			} -			else if (pVecProjectionGeometry) { -				float32 detX = proj->fDetSX + proj->fDetUX*(0.5f + iDetector); -				float32 detY = proj->fDetSY + proj->fDetUY*(0.5f + iDetector); -				alpha = angleBetweenVectors(-proj->fSrcX, -proj->fSrcY, detX - proj->fSrcX, detY - proj->fSrcY); -				t = sin(alpha) * sqrt(proj->fSrcX*proj->fSrcX + proj->fSrcY*proj->fSrcY); +	 +			Dx = proj->fDetSX + (iDetector+0.5f) * proj->fDetUX; +			Dy = proj->fDetSY + (iDetector+0.5f) * proj->fDetUY; + +			Rx = proj->fSrcX - Dx; +			Ry = proj->fSrcY - Dy; + +			bool vertical = fabs(Rx) < fabs(Ry); +			if (vertical) { +				RxOverRy = Rx/Ry; +				lengthPerRow = 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);  			} else { -				assert(false); +				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);  			} -			if (switch_t) t = -t; -			theta = old_theta + alpha; - -			// precalculate sin, cos, 1/cos -			sin_theta = sin(theta); -			cos_theta = cos(theta); -			inv_sin_theta = 1.0f / sin_theta;  -			inv_cos_theta = 1.0f / cos_theta;  - -			// precalculate kernel limits -			lengthPerRow = m_pVolumeGeometry->getPixelLengthY() * inv_cos_theta; -			updatePerRow = sin_theta * inv_cos_theta; -			inv_pixelLengthX = 1.0f / m_pVolumeGeometry->getPixelLengthX(); - -			// precalculate kernel limits -			lengthPerCol = m_pVolumeGeometry->getPixelLengthX() * inv_sin_theta; -			updatePerCol = cos_theta * inv_sin_theta; -			inv_pixelLengthY = 1.0f / m_pVolumeGeometry->getPixelLengthY(); - -			// precalculate S and T -			S = 0.5f - 0.5f * ((updatePerRow < 0) ? -updatePerRow : updatePerRow); -			T = 0.5f - 0.5f * ((updatePerCol < 0) ? -updatePerCol : updatePerCol); +			bool isin = false;  			// vertically -			if (old_theta <= PIdiv4) { -			 -				// calculate x for row 0 -				P = (t - sin_theta * m_pVolumeGeometry->pixelRowToCenterY(0)) * inv_cos_theta; -				x = (P - m_pVolumeGeometry->getWindowMinX()) * inv_pixelLengthX; +			if (vertical) { + +				// calculate c for row 0 +				c = (Dx + (Ey - Dy)*RxOverRy - Ex) * inv_pixelLengthX;  				// for each row -				for (row = 0; row < m_pVolumeGeometry->getGridRowCount(); ++row) { -					 -					// get coords -					x1 = int((x > 0.0f) ? x : x-1.0f); -					x2 = x - x1;  -					x += updatePerRow; +				for (row = 0; row < rowCount; ++row, c += deltac) { -					if (x1 < -1 || x1 > m_pVolumeGeometry->getGridColCount()) continue; +					col = int(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; -					// center -					else if (x2 <= 0.5f+S) { -						if (x1 >= 0 && x1 < m_pVolumeGeometry->getGridColCount()) { -							iVolumeIndex = m_pVolumeGeometry->pixelRowColToIndex(row, x1); -							// POLICY: PIXEL PRIOR + ADD + POSTERIOR -							if (p.pixelPrior(iVolumeIndex)) { -								p.addWeight(iRayIndex, iVolumeIndex, lengthPerRow); -								p.pixelPosterior(iVolumeIndex); -							} -						}					 +						iVolumeIndex = row * colCount + col - 1; +						if (col > 0) { policy_weight(p, iRayIndex, iVolumeIndex, lengthPerRow-weight); } + +						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 { -				// 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 +  } 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 8375726..0adc3e5 100644 --- a/include/astra/Globals.h +++ b/include/astra/Globals.h @@ -142,7 +142,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..67662ad 100644 --- a/include/astra/ParallelBeamBlobKernelProjector2D.inl +++ b/include/astra/ParallelBeamBlobKernelProjector2D.inl @@ -26,186 +26,205 @@ 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(); +	const int detCount = pVecProjectionGeometry->getDetectorCount(); + +	// 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..e516fe1 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,246 @@ void CParallelBeamLineKernelProjector2D::projectSingleRay(int _iProjection, int  //---------------------------------------------------------------------------------------- -// PROJECT BLOCK +// PROJECT BLOCK - vector projection geometry +//  +// Kernel limitations: isotropic pixels (PixelLengthX == PixelLengthY) +// +// For each angle/detector pair: +//  +// Let D=(Dx,Dy) denote the centre of the detector (point) in volume coordinates, and +// let R=(Rx,Ry) denote the direction of the ray (vector). +//  +// For mainly vertical rays (|Rx|<=|Ry|),  +// let E=(Ex,Ey) denote the centre of the most upper left pixel: +//    E = (WindowMinX +  PixelLengthX/2, WindowMaxY - PixelLengthY/2), +// and let F=(Fx,Fy) denote a vector to the next pixel  +//    F = (PixelLengthX, 0) +//  +// The intersection of the ray (D+aR) with the centre line of the upper row of pixels (E+bF) is +//    { Dx + a*Rx = Ex + b*Fx +//    { Dy + a*Ry = Ey + b*Fy +// Solving for (a,b) results in: +//    a = (Ey + b*Fy - Dy)/Ry +//      = (Ey - Dy)/Ry +//    b = (Dx + a*Rx - Ex)/Fx +//      = (Dx + (Ey - Dy)*Rx/Ry - Ex)/Fx +// +// Define c as the x-value of the intersection of the ray with the upper row in pixel coordinates.  +//    c = b  +// +// The intersection of the ray (D+aR) with the centre line of the second row of pixels (E'+bF) with +//    E'=(WindowMinX + PixelLengthX/2, WindowMaxY - 3*PixelLengthY/2) +// expressed in x-value pixel coordinates is +//    c' = (Dx + (Ey' - Dy)*Rx/Ry - Ex)/Fx. +// And thus: +//    deltac = c' - c = (Dx + (Ey' - Dy)*Rx/Ry - Ex)/Fx - (Dx + (Ey - Dy)*Rx/Ry - Ex)/Fx +//                    = [(Ey' - Dy)*Rx/Ry - (Ey - Dy)*Rx/Ry]/Fx +//                    = [Ey' - Ey]*(Rx/Ry)/Fx +//                    = [Ey' - Ey]*(Rx/Ry)/Fx +//                    = -PixelLengthY*(Rx/Ry)/Fx. +//  +// Given c on a certain row, its closest pixel (col), and the distance (offset) to it, can be found:  +//    col = floor(c+1/2) +//    offset = c - col +// +// The index of this pixel is +//    volumeIndex = row * colCount + col +// +// The projection kernel is defined by +// +//         _____       LengthPerRow +//       /|  |  |\ +//      / |  |  | \ +//   __/  |  |  |  \__ 0 +//    -T -S  0  S  T +// +// with S = 1/2 - 1/2*|Rx/Ry|, T = 1/2 + 1/2*|Rx/Ry|, and LengthPerRow = pixelLengthX * sqrt(Rx^2+Ry^2) / |Ry| +// +// And thus +//                            { (offset+T)/(T-S) * LengthPerRow    if  -T <= offset < S +//    W_(rayIndex,volIndex) = { LengthPerRow                       if  -S <= offset <= S +//                            { (offset-S)/(T-S) * LengthPerRow    if   S < offset <= T +// +// If -T <= offset < S, the weight for the pixel directly to the left is +//    W_(rayIndex,volIndex-1) = LengthPerRow - (offset+T)/(T-S) * LengthPerRow, +// and if S < offset <= T, the weight for the pixel directly to the right is +//    W_(rayIndex,volIndex+1) = LengthPerRow - (offset-S)/(T-S) * LengthPerRow. +// +// +// Mainly horizontal rays (|Rx|<=|Ry|) are handled in a similar fashion: +// +//    E = (WindowMinX +  PixelLengthX/2, WindowMaxY - PixelLengthY/2), +//    F = (0, -PixelLengthX) +// +//    a = (Ex + b*Fx - Dx)/Rx = (Ex - Dx)/Rx +//    b = (Dy + a*Ry - Ey)/Fy = (Dy + (Ex - Dx)*Ry/Rx - Ey)/Fy +//    r = b +//    deltar = PixelLengthX*(Ry/Rx)/Fy. +//    row = floor(r+1/2) +//    offset = r - row +//    S = 1/2 - 1/2*|Ry/Rx| +//    T = 1/2 + 1/2*|Ry/Rx| +//    LengthPerCol = pixelLengthY * sqrt(Rx^2+Ry^2) / |Rx| +// +//                            { (offset+T)/(T-S) * LengthPerCol    if  -T <= offset < S +//    W_(rayIndex,volIndex) = { LengthPerCol                       if  -S <= offset <= S +//                            { (offset-S)/(T-S) * LengthPerCol    if   S < offset <= T +// +//    W_(rayIndex,volIndex-colcount) = LengthPerCol - (offset+T)/(T-S) * LengthPerCol +//    W_(rayIndex,volIndex+colcount) = LengthPerCol - (offset-S)/(T-S) * LengthPerCol +//  template <typename Policy>  void CParallelBeamLineKernelProjector2D::projectBlock_internal(int _iProjFrom, int _iProjTo, int _iDetFrom, int _iDetTo, Policy& p)  { -	// variables -	float32 theta, sin_theta, cos_theta, inv_sin_theta, inv_cos_theta, S, T, t, I, P, x, x2; -	float32 lengthPerRow, updatePerRow, inv_pixelLengthX, lengthPerCol, updatePerCol, inv_pixelLengthY; -	int iVolumeIndex, iRayIndex, row, col, iAngle, iDetector, x1; -	bool switch_t; +	// get vector geometry +	const CParallelVecProjectionGeometry2D* pVecProjectionGeometry; +	if (dynamic_cast<CParallelProjectionGeometry2D*>(m_pProjectionGeometry)) { +		pVecProjectionGeometry = dynamic_cast<CParallelProjectionGeometry2D*>(m_pProjectionGeometry)->toVectorGeometry(); +	} else { +		pVecProjectionGeometry = dynamic_cast<CParallelVecProjectionGeometry2D*>(m_pProjectionGeometry); +	} + +	// precomputations +	const float32 pixelLengthX = m_pVolumeGeometry->getPixelLengthX(); +	const float32 pixelLengthY = m_pVolumeGeometry->getPixelLengthY(); +	const float32 inv_pixelLengthX = 1.0f / pixelLengthX; +	const float32 inv_pixelLengthY = 1.0f / pixelLengthY; +	const int colCount = m_pVolumeGeometry->getGridColCount(); +	const int rowCount = m_pVolumeGeometry->getGridRowCount(); +	const int detCount = pVecProjectionGeometry->getDetectorCount();  	// loop angles -	for (iAngle = _iProjFrom; iAngle < _iProjTo; ++iAngle) { - -		// get theta -		theta = m_pProjectionGeometry->getProjectionAngle(iAngle); -		switch_t = false; -		if (theta >= 7*PIdiv4) theta -= 2*PI; -		if (theta >= 3*PIdiv4) { -			theta -= PI; -			switch_t = true; +	for (int iAngle = _iProjFrom; iAngle < _iProjTo; ++iAngle) { + +		// variables +		float32 Dx, Dy, Ex, Ey, S, T, weight, c, r, deltac, deltar, offset; +		float32 RxOverRy, RyOverRx, lengthPerRow, lengthPerCol, invTminSTimesLengthPerRow, invTminSTimesLengthPerCol; +		int iVolumeIndex, iRayIndex, row, col, iDetector; + +		const SParProjection * proj = &pVecProjectionGeometry->getProjectionVectors()[iAngle]; + +		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 +296,7 @@ void CParallelBeamLineKernelProjector2D::projectBlock_internal(int _iProjFrom, i  		} // end loop detector  	} // end loop angles -} +	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..d2c529f 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,128 @@ 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(); +	const int detCount = pVecProjectionGeometry->getDetectorCount();  	// loop angles -	for (iAngle = _iProjFrom; iAngle < _iProjTo; ++iAngle) { - -		// get theta -		theta = m_pProjectionGeometry->getProjectionAngle(iAngle); -		switch_t = false; -		if (theta >= 7*PIdiv4) theta -= 2*PI; -		if (theta >= 3*PIdiv4) { -			theta -= PI; -			switch_t = true; +	for (int iAngle = _iProjFrom; iAngle < _iProjTo; ++iAngle) { + +		// variables +		float32 Dx, Dy, Ex, Ey, x, y, c, r, deltac, deltar, offset; +		float32 RxOverRy, RyOverRx, lengthPerRow, lengthPerCol; +		int iVolumeIndex, iRayIndex, row, col, iDetector; + +		const SParProjection * proj = &pVecProjectionGeometry->getProjectionVectors()[iAngle]; + +		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 +179,54 @@ void CParallelBeamLinearKernelProjector2D::projectBlock_internal(int _iProjFrom,  			// POLICY: RAY PRIOR  			if (!p.rayPrior(iRayIndex)) continue; -			// get t -			t = m_pProjectionGeometry->indexToDetectorOffset(iDetector); -			if (switch_t) { -				t = -t; -			} +			Dx = proj->fDetSX + (iDetector+0.5f) * proj->fDetUX; +			Dy = proj->fDetSY + (iDetector+0.5f) * proj->fDetUY; + +			bool isin = false;  			// vertically -			if (theta <= PIdiv4) { -			 -				// calculate x for row 0 -				P = (t - sin_theta * m_pVolumeGeometry->pixelRowToCenterY(0)) * inv_cos_theta; -				x = m_pVolumeGeometry->coordXToColF(P) - 0.5f; +			if (vertical) { + +				// calculate c for row 0 +				c = (Dx + (Ey - Dy)*RxOverRy - Ex) * inv_pixelLengthX; + +				// loop rows +				for (row = 0; row < rowCount; ++row, c += deltac) { -				// for each row -				for (row = 0; row < m_pVolumeGeometry->getGridRowCount(); ++row) { +					col = int(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 +236,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..4f828f0 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> @@ -48,250 +48,243 @@ void CParallelBeamStripKernelProjector2D::projectSingleRay(int _iProjection, int  //----------------------------------------------------------------------------------------  // PROJECT BLOCK +//  +// Kernel limitations: isotropic pixels (PixelLengthX == PixelLengthY) +// +// For each angle/detector pair: +//  +// Let DL=(DLx,DLy) denote the left of the detector (point) in volume coordinates, and +// Let DR=(DRx,DRy) denote the right of the detector (point) in volume coordinates, and +// let R=(Rx,Ry) denote the direction of the ray (vector). +//  +// For mainly vertical rays (|Rx|<=|Ry|),  +// let E=(Ex,Ey) denote the centre of the most upper left pixel: +//    E = (WindowMinX +  PixelLengthX/2, WindowMaxY - PixelLengthY/2), +// and let F=(Fx,Fy) denote a vector to the next pixel  +//    F = (PixelLengthX, 0) +//  +// The intersection of the left edge of the strip (DL+aR) with the centre line of the upper row of pixels (E+bF) is +//    { DLx + a*Rx = Ex + b*Fx +//    { DLy + a*Ry = Ey + b*Fy +// Solving for (a,b) results in: +//    a = (Ey + b*Fy - DLy)/Ry +//      = (Ey - DLy)/Ry +//    b = (DLx + a*Rx - Ex)/Fx +//      = (DLx + (Ey - DLy)*Rx/Ry - Ex)/Fx +// +// Define cL as the x-value of the intersection of the left edge of the strip with the upper row in pixel coordinates.  +//    cL = b  +// +// cR, the x-value of the intersection of the right edge of the strip with the upper row in pixel coordinates can be found similarly. +// +// The intersection of the ray (DL+aR) with the left line of the second row of pixels (E'+bF) with +//    E'=(WindowMinX + PixelLengthX/2, WindowMaxY - 3*PixelLengthY/2) +// expressed in x-value pixel coordinates is +//    cL' = (DLx + (Ey' - DLy)*Rx/Ry - Ex)/Fx. +// And thus: +//    deltac = cL' - cL = (DLx + (Ey' - DLy)*Rx/Ry - Ex)/Fx - (DLx + (Ey - DLy)*Rx/Ry - Ex)/Fx +//                      = [(Ey' - DLy)*Rx/Ry - (Ey - DLy)*Rx/Ry]/Fx +//                      = [Ey' - Ey]*(Rx/Ry)/Fx +//                      = [Ey' - Ey]*(Rx/Ry)/Fx +//                      = -PixelLengthY*(Rx/Ry)/Fx. +// +// The projection weight for a certain pixel is defined by the area between two points of  +// +//         _____       LengthPerRow +//       /|  |  |\ +//      / |  |  | \ +//   __/  |  |  |  \__ 0 +//    -T -S  0  S  T +// with S = 1/2 - 1/2*|Rx/Ry|, T = 1/2 + 1/2*|Rx/Ry|, and LengthPerRow = pixelLengthX * sqrt(Rx^2+Ry^2) / |Ry| +//  +// For a certain row, all columns that are 'hit' by this kernel lie in the interval +//    (col_left, col_right) = (floor(cL-1/2+S), floor(cR+3/2-S)) +// +// The offsets for both is +//    (offsetL, offsetR) = (cL - floor(col_left), cR - floor(col_left)) +// +// The projection weight is found by the difference between the integrated values of the kernel +//         offset <= -T   Kernel = 0 +//    -T < offset <= -S   Kernel = PixelArea/2*(T+offset)^2/(T-S) +//    -S < offset <=  S   Kernel = PixelArea/2 + offset +//     S < offset <=  T   Kernel = PixelArea - PixelArea/2*(T-offset)^2/(T-S) +//     T <= offset:       Kernel = PixelArea +//  template <typename Policy>  void CParallelBeamStripKernelProjector2D::projectBlock_internal(int _iProjFrom, int _iProjTo, int _iDetFrom, int _iDetTo, Policy& p)  { -	ASTRA_ASSERT(m_bIsInitialized); - -	// Some variables -	float32 theta, t; -	int row, col; -	int iAngle; -	int iDetector; -	float32 res; -	float32 PL, PLimitL, PLimitR; -	float32 xL, xR, XLimitL, XLimitR; -	int x1L,x1R; -	float32 x2L, x2R, updateX; -	int iVolumeIndex, iRayIndex; -	 -	float32 sin_theta, cos_theta, inv_sin_theta, inv_cos_theta; -	float32 fabs_sin_theta, fabs_cos_theta, fabs_inv_sin_theta, fabs_inv_cos_theta; -	float32 PW, PH, DW, inv_PW, inv_PH; -	float32 S, T, U, V, inv_4T; +	// get vector geometry +	const CParallelVecProjectionGeometry2D* pVecProjectionGeometry; +	if (dynamic_cast<CParallelProjectionGeometry2D*>(m_pProjectionGeometry)) { +		pVecProjectionGeometry = dynamic_cast<CParallelProjectionGeometry2D*>(m_pProjectionGeometry)->toVectorGeometry(); +	} else { +		pVecProjectionGeometry = dynamic_cast<CParallelVecProjectionGeometry2D*>(m_pProjectionGeometry); +	} + +	// precomputations +	const float32 pixelLengthX = m_pVolumeGeometry->getPixelLengthX(); +	const float32 pixelLengthY = m_pVolumeGeometry->getPixelLengthY(); +	const float32 pixelArea = pixelLengthX * pixelLengthY; +	const float32 inv_pixelLengthX = 1.0f / pixelLengthX; +	const float32 inv_pixelLengthY = 1.0f / pixelLengthY; +	const int colCount = m_pVolumeGeometry->getGridColCount(); +	const int rowCount = m_pVolumeGeometry->getGridRowCount(); +	const int detCount = pVecProjectionGeometry->getDetectorCount();  	// loop angles -	for (iAngle = _iProjFrom; iAngle < _iProjTo; ++iAngle) { -		 -		// get values -		theta = m_pProjectionGeometry->getProjectionAngle(iAngle); -		bool switch_t = false; -		if (theta >= 7*PIdiv4) theta -= 2*PI; -		if (theta >= 3*PIdiv4) { -			theta -= PI; -			switch_t = true; -		} - -		// Precalculate sin, cos, 1/cos -		sin_theta = sin(theta); -		cos_theta = cos(theta); -		inv_cos_theta = 1.0f / cos_theta;  -		inv_sin_theta = 1.0f / sin_theta; - -		fabs_sin_theta = (sin_theta < 0.0f) ? -sin_theta : sin_theta; -		fabs_cos_theta = (cos_theta < 0.0f) ? -cos_theta : cos_theta; -		fabs_inv_cos_theta = (inv_cos_theta < 0.0f) ? -inv_cos_theta : inv_cos_theta; -		fabs_inv_sin_theta = (inv_sin_theta < 0.0f) ? -inv_sin_theta : inv_sin_theta; - -		// Other precalculations -		PW = m_pVolumeGeometry->getPixelLengthX(); -		PH = m_pVolumeGeometry->getPixelLengthY(); -		DW = m_pProjectionGeometry->getDetectorWidth(); -		inv_PW = 1.0f / PW; -		inv_PH = 1.0f / PH; - -		// [-45?,45?] and [135?,225?] -		if (theta < PIdiv4) { - -			// Precalculate kernel limits -			S = -0.5f * fabs_sin_theta * fabs_inv_cos_theta; -			T = -S; -			U = 1.0f + S; -			V = 1.0f - S; -			inv_4T = 0.25f / T; - -			updateX = sin_theta * inv_cos_theta; - -			// loop detectors -			for (iDetector = _iDetFrom; iDetector < _iDetTo; ++iDetector) { -			 -				iRayIndex = iAngle * m_pProjectionGeometry->getDetectorCount() + iDetector; - -				// POLICY: RAY PRIOR -				if (!p.rayPrior(iRayIndex)) continue; - -				// get t -				t = m_pProjectionGeometry->indexToDetectorOffset(iDetector); -				if (switch_t) t = -t; -			 -				// calculate left strip extremes (volume coordinates) -				PL = (t - sin_theta * m_pVolumeGeometry->pixelRowToCenterY(0) - DW*0.5f) * inv_cos_theta; -				PLimitL = PL - 0.5f * fabs_sin_theta * fabs_inv_cos_theta * PH; -				PLimitR = PLimitL + DW * inv_cos_theta + PH * fabs_sin_theta * fabs_inv_cos_theta;  - -				// calculate strip extremes (pixel coordinates) -				XLimitL = (PLimitL - m_pVolumeGeometry->getWindowMinX()) * inv_PW; -				XLimitR = (PLimitR - m_pVolumeGeometry->getWindowMinX()) * inv_PW; -				xL = (PL - m_pVolumeGeometry->getWindowMinX()) * inv_PW; -				xR = xL + (DW * inv_cos_theta) * inv_PW; -	 -				// for each row -				for (row = 0; row < m_pVolumeGeometry->getGridRowCount(); ++row) { -				 -					// get strip extremes in column indices -					x1L = int((XLimitL > 0.0f) ? XLimitL : XLimitL-1.0f); -					x1R = int((XLimitR > 0.0f) ? XLimitR : XLimitR-1.0f); - -					// get coords w.r.t leftmost column hit by strip -					x2L = xL - x1L;  -					x2R = xR - x1L; -					 -					// update strip extremes for the next row -					XLimitL += updateX;  -					XLimitR += updateX; -					xL += updateX;  -					xR += updateX;  - -					// for each affected col -					for (col = x1L; col <= x1R; ++col) { - -						if (col < 0 || col >= m_pVolumeGeometry->getGridColCount()) { x2L -= 1.0f; x2R -= 1.0f;	continue; } - -						iVolumeIndex = m_pVolumeGeometry->pixelRowColToIndex(row, col); -						// POLICY: PIXEL PRIOR -						if (!p.pixelPrior(iVolumeIndex)) { x2L -= 1.0f; x2R -= 1.0f; continue; } -						 -						// right -						if (x2R >= V)		res = 1.0f; -						else if (x2R > U)	res = x2R - (x2R-U)*(x2R-U)*inv_4T; -						else if (x2R >= T)	res = x2R; -						else if (x2R > S)	res = (x2R-S)*(x2R-S) * inv_4T; -						else				{ x2L -= 1.0f; x2R -= 1.0f;	continue; } -								 -						// left -						if (x2L <= S)		{} // - 0.0f -						else if (x2L < T)	res -= (x2L-S)*(x2L-S) * inv_4T; -						else if (x2L <= U)	res -= x2L; -						else if (x2L < V)	res -= x2L - (x2L-U)*(x2L-U)*inv_4T; -						else				{ x2L -= 1.0f; x2R -= 1.0f;	continue; } - -						// POLICY: ADD -						p.addWeight(iRayIndex, iVolumeIndex, PW*PH * res); - -						// POLICY: PIXEL POSTERIOR -						p.pixelPosterior(iVolumeIndex); - -						x2L -= 1.0f;		 -						x2R -= 1.0f; - -					} // end col loop - -				} // end row loop - -				// POLICY: RAY POSTERIOR -				p.rayPosterior(iRayIndex); - -			}	// end detector loop - -		// [45?,135?] and [225?,315?] -		// horizontaly +	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 c091963..bba47f3 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 53d98e0..4ddaf0c 100644 --- a/python/astra/creators.py +++ b/python/astra/creators.py @@ -154,6 +154,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)``: @@ -241,6 +248,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/src/CudaFDKAlgorithm3D.cpp b/src/CudaFDKAlgorithm3D.cpp index d503351..6616a85 100644 --- a/src/CudaFDKAlgorithm3D.cpp +++ b/src/CudaFDKAlgorithm3D.cpp @@ -155,7 +155,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 7e2df79..2829b7d 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) @@ -91,54 +64,28 @@ bool CCudaFilteredBackProjectionAlgorithm::initialize(const Config& _cfg)  	// if already initialized, clear first  	if (m_bIsInitialized)  	{ +#warning FIXME Necessary?  		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 +134,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 +161,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 +190,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); +void CCudaFilteredBackProjectionAlgorithm::initCUDAAlgorithm() +{ +	CCudaReconstructionAlgorithm2D::initCUDAAlgorithm(); -		ok &= m_pFBP->init(m_iGPUIndex); -		ASTRA_ASSERT(ok); +	astraCUDA::FBP* pFBP = dynamic_cast<astraCUDA::FBP*>(m_pAlgo); -		ok &= m_pFBP->setSinogram(m_pSinogram->getDataConst(), iDetectorCount); +	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); - -		ok &= m_pFBP->setFilter(m_eFilter, m_pfFilter, m_iFilterWidth, m_fFilterD, m_fFilterParameter); -		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 @@ -394,16 +248,6 @@ static int calcNextPowerOfTwo(int _iValue)  	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; @@ -517,12 +361,3 @@ E_FBPFILTER CCudaFilteredBackProjectionAlgorithm::_convertStringToFilter(const c  	return output;  } -void CCudaFilteredBackProjectionAlgorithm::testGenFilter(E_FBPFILTER _eFilter, float _fD, int _iProjectionCount, cufftComplex * _pFilter, int _iFFTRealDetectorCount, int _iFFTFourierDetectorCount) -{ -	genFilter(_eFilter, _fD, _iProjectionCount, _pFilter, _iFFTRealDetectorCount, _iFFTFourierDetectorCount); -} - -int CCudaFilteredBackProjectionAlgorithm::getGPUCount() -{ -	return 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/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 f1fa311..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); @@ -155,7 +155,7 @@ BOOST_AUTO_TEST_CASE( testParallelBeamLinearKernelProjector2D_Rectangles )  				            pPix[i].m_iIndex / volGeom.getGridColCount(),  				            iDet,  				            projGeom.getProjectionAngle(0)); -				BOOST_CHECK_SMALL( pPix[i].m_fWeight - fTest, 0.00037f); +				BOOST_CHECK_SMALL( pPix[i].m_fWeight - fTest, 0.0004f);  				fW += pPix[i].m_fWeight;  			} | 
