/* ----------------------------------------------------------------------- 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 . ----------------------------------------------------------------------- $Id$ */ #include #include #include "cgls3d.h" #include "sirt3d.h" #include "util3d.h" #include "cone_fp.h" #include "cone_bp.h" #include "par3d_fp.h" #include "par3d_bp.h" #include "fdk.h" #include "arith3d.h" #include "astra3d.h" #include using namespace astraCUDA3d; namespace astra { enum CUDAProjectionType3d { PROJ_PARALLEL, PROJ_CONE }; static SConeProjection* genConeProjections(unsigned int iProjAngles, unsigned int iProjU, unsigned int iProjV, double fOriginSourceDistance, double fOriginDetectorDistance, double fDetUSize, double fDetVSize, const float *pfAngles) { SConeProjection base; base.fSrcX = 0.0f; base.fSrcY = -fOriginSourceDistance; base.fSrcZ = 0.0f; base.fDetSX = iProjU * fDetUSize * -0.5f; base.fDetSY = fOriginDetectorDistance; base.fDetSZ = iProjV * fDetVSize * -0.5f; base.fDetUX = fDetUSize; base.fDetUY = 0.0f; base.fDetUZ = 0.0f; base.fDetVX = 0.0f; base.fDetVY = 0.0f; base.fDetVZ = fDetVSize; SConeProjection* p = new SConeProjection[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); p[i].f##name##Z = base.f##name##Z; } 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]); ROTATE0(DetV, i, pfAngles[i]); } #undef ROTATE0 return p; } static SPar3DProjection* genPar3DProjections(unsigned int iProjAngles, unsigned int iProjU, unsigned int iProjV, double fDetUSize, double fDetVSize, const float *pfAngles) { SPar3DProjection base; base.fRayX = 0.0f; base.fRayY = 1.0f; base.fRayZ = 0.0f; base.fDetSX = iProjU * fDetUSize * -0.5f; base.fDetSY = 0.0f; base.fDetSZ = iProjV * fDetVSize * -0.5f; base.fDetUX = fDetUSize; base.fDetUY = 0.0f; base.fDetUZ = 0.0f; base.fDetVX = 0.0f; base.fDetVY = 0.0f; base.fDetVZ = fDetVSize; SPar3DProjection* p = new SPar3DProjection[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); p[i].f##name##Z = base.f##name##Z; } while(0) for (unsigned int i = 0; i < iProjAngles; ++i) { ROTATE0(Ray, i, pfAngles[i]); ROTATE0(DetS, i, pfAngles[i]); ROTATE0(DetU, i, pfAngles[i]); ROTATE0(DetV, i, pfAngles[i]); } #undef ROTATE0 return p; } class AstraSIRT3d_internal { public: SDimensions3D dims; CUDAProjectionType3d projType; float* angles; float fOriginSourceDistance; float fOriginDetectorDistance; float fSourceZ; float fDetSize; SConeProjection* projs; SPar3DProjection* parprojs; float fPixelSize; bool initialized; bool setStartReconstruction; bool useVolumeMask; bool useSinogramMask; // Input/output cudaPitchedPtr D_projData; cudaPitchedPtr D_volumeData; cudaPitchedPtr D_maskData; cudaPitchedPtr D_smaskData; SIRT sirt; }; AstraSIRT3d::AstraSIRT3d() { pData = new AstraSIRT3d_internal(); pData->angles = 0; pData->D_projData.ptr = 0; pData->D_volumeData.ptr = 0; pData->D_maskData.ptr = 0; pData->D_smaskData.ptr = 0; pData->dims.iVolX = 0; pData->dims.iVolY = 0; pData->dims.iVolZ = 0; pData->dims.iProjAngles = 0; pData->dims.iProjU = 0; pData->dims.iProjV = 0; pData->dims.iRaysPerDetDim = 1; pData->dims.iRaysPerVoxelDim = 1; pData->projs = 0; pData->initialized = false; pData->setStartReconstruction = false; pData->useVolumeMask = false; pData->useSinogramMask = false; } AstraSIRT3d::~AstraSIRT3d() { delete[] pData->angles; pData->angles = 0; delete[] pData->projs; pData->projs = 0; cudaFree(pData->D_projData.ptr); pData->D_projData.ptr = 0; cudaFree(pData->D_volumeData.ptr); pData->D_volumeData.ptr = 0; cudaFree(pData->D_maskData.ptr); pData->D_maskData.ptr = 0; cudaFree(pData->D_smaskData.ptr); pData->D_smaskData.ptr = 0; delete pData; pData = 0; } bool AstraSIRT3d::setReconstructionGeometry(unsigned int iVolX, unsigned int iVolY, unsigned int iVolZ/*, float fPixelSize = 1.0f*/) { if (pData->initialized) return false; pData->dims.iVolX = iVolX; pData->dims.iVolY = iVolY; pData->dims.iVolZ = iVolZ; return (iVolX > 0 && iVolY > 0 && iVolZ > 0); } bool AstraSIRT3d::setPar3DGeometry(unsigned int iProjAngles, unsigned int iProjU, unsigned int iProjV, const SPar3DProjection* projs) { if (pData->initialized) return false; pData->dims.iProjAngles = iProjAngles; pData->dims.iProjU = iProjU; pData->dims.iProjV = iProjV; if (iProjAngles == 0 || iProjU == 0 || iProjV == 0 || projs == 0) return false; pData->parprojs = new SPar3DProjection[iProjAngles]; memcpy(pData->parprojs, projs, iProjAngles * sizeof(projs[0])); pData->projType = PROJ_PARALLEL; return true; } bool AstraSIRT3d::setPar3DGeometry(unsigned int iProjAngles, unsigned int iProjU, unsigned int iProjV, float fDetUSize, float fDetVSize, const float *pfAngles) { if (pData->initialized) return false; if (iProjAngles == 0 || iProjU == 0 || iProjV == 0 || pfAngles == 0) return false; SPar3DProjection* p = genPar3DProjections(iProjAngles, iProjU, iProjV, fDetUSize, fDetVSize, pfAngles); pData->dims.iProjAngles = iProjAngles; pData->dims.iProjU = iProjU; pData->dims.iProjV = iProjV; pData->parprojs = p; pData->projType = PROJ_PARALLEL; return true; } bool AstraSIRT3d::setConeGeometry(unsigned int iProjAngles, unsigned int iProjU, unsigned int iProjV, const SConeProjection* projs) { if (pData->initialized) return false; pData->dims.iProjAngles = iProjAngles; pData->dims.iProjU = iProjU; pData->dims.iProjV = iProjV; if (iProjAngles == 0 || iProjU == 0 || iProjV == 0 || projs == 0) return false; pData->projs = new SConeProjection[iProjAngles]; memcpy(pData->projs, projs, iProjAngles * sizeof(projs[0])); pData->projType = PROJ_CONE; return true; } bool AstraSIRT3d::setConeGeometry(unsigned int iProjAngles, unsigned int iProjU, unsigned int iProjV, float fOriginSourceDistance, float fOriginDetectorDistance, float fDetUSize, float fDetVSize, const float *pfAngles) { if (pData->initialized) return false; if (iProjAngles == 0 || iProjU == 0 || iProjV == 0 || pfAngles == 0) return false; SConeProjection* p = genConeProjections(iProjAngles, iProjU, iProjV, fOriginSourceDistance, fOriginDetectorDistance, fDetUSize, fDetVSize, pfAngles); pData->dims.iProjAngles = iProjAngles; pData->dims.iProjU = iProjU; pData->dims.iProjV = iProjV; pData->projs = p; pData->projType = PROJ_CONE; return true; } bool AstraSIRT3d::enableSuperSampling(unsigned int iVoxelSuperSampling, unsigned int iDetectorSuperSampling) { if (pData->initialized) return false; if (iVoxelSuperSampling == 0 || iDetectorSuperSampling == 0) return false; pData->dims.iRaysPerVoxelDim = iVoxelSuperSampling; pData->dims.iRaysPerDetDim = iDetectorSuperSampling; return true; } bool AstraSIRT3d::enableVolumeMask() { if (pData->initialized) return false; bool ok = pData->sirt.enableVolumeMask(); pData->useVolumeMask = ok; return ok; } bool AstraSIRT3d::enableSinogramMask() { if (pData->initialized) return false; bool ok = pData->sirt.enableSinogramMask(); pData->useSinogramMask = ok; return ok; } bool AstraSIRT3d::setGPUIndex(int index) { if (index != -1) { cudaSetDevice(index); cudaError_t err = cudaGetLastError(); // Ignore errors caused by calling cudaSetDevice multiple times if (err != cudaSuccess && err != cudaErrorSetOnActiveProcess) return false; } return true; } bool AstraSIRT3d::init() { if (pData->initialized) return false; if (pData->dims.iVolX == 0 || pData->dims.iProjAngles == 0) return false; bool ok; if (pData->projType == PROJ_PARALLEL) { ok = pData->sirt.setPar3DGeometry(pData->dims, pData->parprojs); } else { ok = pData->sirt.setConeGeometry(pData->dims, pData->projs); } if (!ok) return false; ok = pData->sirt.init(); if (!ok) return false; pData->D_volumeData = allocateVolumeData(pData->dims); ok = pData->D_volumeData.ptr; if (!ok) return false; pData->D_projData = allocateProjectionData(pData->dims); ok = pData->D_projData.ptr; if (!ok) { cudaFree(pData->D_volumeData.ptr); pData->D_volumeData.ptr = 0; return false; } if (pData->useVolumeMask) { pData->D_maskData = allocateVolumeData(pData->dims); ok = pData->D_maskData.ptr; if (!ok) { cudaFree(pData->D_volumeData.ptr); cudaFree(pData->D_projData.ptr); pData->D_volumeData.ptr = 0; pData->D_projData.ptr = 0; return false; } } if (pData->useSinogramMask) { pData->D_smaskData = allocateProjectionData(pData->dims); ok = pData->D_smaskData.ptr; if (!ok) { cudaFree(pData->D_volumeData.ptr); cudaFree(pData->D_projData.ptr); cudaFree(pData->D_maskData.ptr); pData->D_volumeData.ptr = 0; pData->D_projData.ptr = 0; pData->D_maskData.ptr = 0; return false; } } pData->initialized = true; return true; } bool AstraSIRT3d::setMinConstraint(float fMin) { if (!pData->initialized) return false; return pData->sirt.setMinConstraint(fMin); } bool AstraSIRT3d::setMaxConstraint(float fMax) { if (!pData->initialized) return false; return pData->sirt.setMaxConstraint(fMax); } bool AstraSIRT3d::setSinogram(const float* pfSinogram, unsigned int iSinogramPitch) { if (!pData->initialized) return false; if (!pfSinogram) return false; bool ok = copyProjectionsToDevice(pfSinogram, pData->D_projData, pData->dims, iSinogramPitch); if (!ok) return false; ok = pData->sirt.setBuffers(pData->D_volumeData, pData->D_projData); if (!ok) return false; pData->setStartReconstruction = false; return true; } bool AstraSIRT3d::setVolumeMask(const float* pfMask, unsigned int iMaskPitch) { if (!pData->initialized) return false; if (!pData->useVolumeMask) return false; if (!pfMask) return false; bool ok = copyVolumeToDevice(pfMask, pData->D_maskData, pData->dims, iMaskPitch); if (!ok) return false; ok = pData->sirt.setVolumeMask(pData->D_maskData); if (!ok) return false; return true; } bool AstraSIRT3d::setSinogramMask(const float* pfMask, unsigned int iMaskPitch) { if (!pData->initialized) return false; if (!pData->useSinogramMask) return false; if (!pfMask) return false; bool ok = copyProjectionsToDevice(pfMask, pData->D_smaskData, pData->dims, iMaskPitch); if (!ok) return false; ok = pData->sirt.setSinogramMask(pData->D_smaskData); if (!ok) return false; return true; } bool AstraSIRT3d::setStartReconstruction(const float* pfReconstruction, unsigned int iReconstructionPitch) { if (!pData->initialized) return false; if (!pfReconstruction) return false; bool ok = copyVolumeToDevice(pfReconstruction, pData->D_volumeData, pData->dims, iReconstructionPitch); if (!ok) return false; pData->setStartReconstruction = true; return true; } bool AstraSIRT3d::iterate(unsigned int iIterations) { if (!pData->initialized) return false; if (!pData->setStartReconstruction) zeroVolumeData(pData->D_volumeData, pData->dims); bool ok = pData->sirt.iterate(iIterations); if (!ok) return false; return true; } bool AstraSIRT3d::getReconstruction(float* pfReconstruction, unsigned int iReconstructionPitch) const { if (!pData->initialized) return false; bool ok = copyVolumeFromDevice(pfReconstruction, pData->D_volumeData, pData->dims, iReconstructionPitch); if (!ok) return false; return true; } void AstraSIRT3d::signalAbort() { if (!pData->initialized) return; pData->sirt.signalAbort(); } float AstraSIRT3d::computeDiffNorm() { if (!pData->initialized) return 0.0f; // FIXME: Error? return pData->sirt.computeDiffNorm(); } class AstraCGLS3d_internal { public: SDimensions3D dims; CUDAProjectionType3d projType; float* angles; float fOriginSourceDistance; float fOriginDetectorDistance; float fSourceZ; float fDetSize; SConeProjection* projs; SPar3DProjection* parprojs; float fPixelSize; bool initialized; bool setStartReconstruction; bool useVolumeMask; bool useSinogramMask; // Input/output cudaPitchedPtr D_projData; cudaPitchedPtr D_volumeData; cudaPitchedPtr D_maskData; cudaPitchedPtr D_smaskData; CGLS cgls; }; AstraCGLS3d::AstraCGLS3d() { pData = new AstraCGLS3d_internal(); pData->angles = 0; pData->D_projData.ptr = 0; pData->D_volumeData.ptr = 0; pData->D_maskData.ptr = 0; pData->D_smaskData.ptr = 0; pData->dims.iVolX = 0; pData->dims.iVolY = 0; pData->dims.iVolZ = 0; pData->dims.iProjAngles = 0; pData->dims.iProjU = 0; pData->dims.iProjV = 0; pData->dims.iRaysPerDetDim = 1; pData->dims.iRaysPerVoxelDim = 1; pData->projs = 0; pData->initialized = false; pData->setStartReconstruction = false; pData->useVolumeMask = false; pData->useSinogramMask = false; } AstraCGLS3d::~AstraCGLS3d() { delete[] pData->angles; pData->angles = 0; delete[] pData->projs; pData->projs = 0; cudaFree(pData->D_projData.ptr); pData->D_projData.ptr = 0; cudaFree(pData->D_volumeData.ptr); pData->D_volumeData.ptr = 0; cudaFree(pData->D_maskData.ptr); pData->D_maskData.ptr = 0; cudaFree(pData->D_smaskData.ptr); pData->D_smaskData.ptr = 0; delete pData; pData = 0; } bool AstraCGLS3d::setReconstructionGeometry(unsigned int iVolX, unsigned int iVolY, unsigned int iVolZ/*, float fPixelSize = 1.0f*/) { if (pData->initialized) return false; pData->dims.iVolX = iVolX; pData->dims.iVolY = iVolY; pData->dims.iVolZ = iVolZ; return (iVolX > 0 && iVolY > 0 && iVolZ > 0); } bool AstraCGLS3d::setPar3DGeometry(unsigned int iProjAngles, unsigned int iProjU, unsigned int iProjV, const SPar3DProjection* projs) { if (pData->initialized) return false; pData->dims.iProjAngles = iProjAngles; pData->dims.iProjU = iProjU; pData->dims.iProjV = iProjV; if (iProjAngles == 0 || iProjU == 0 || iProjV == 0 || projs == 0) return false; pData->parprojs = new SPar3DProjection[iProjAngles]; memcpy(pData->parprojs, projs, iProjAngles * sizeof(projs[0])); pData->projType = PROJ_PARALLEL; return true; } bool AstraCGLS3d::setPar3DGeometry(unsigned int iProjAngles, unsigned int iProjU, unsigned int iProjV, float fDetUSize, float fDetVSize, const float *pfAngles) { if (pData->initialized) return false; if (iProjAngles == 0 || iProjU == 0 || iProjV == 0 || pfAngles == 0) return false; SPar3DProjection* p = genPar3DProjections(iProjAngles, iProjU, iProjV, fDetUSize, fDetVSize, pfAngles); pData->dims.iProjAngles = iProjAngles; pData->dims.iProjU = iProjU; pData->dims.iProjV = iProjV; pData->parprojs = p; pData->projType = PROJ_PARALLEL; return true; } bool AstraCGLS3d::setConeGeometry(unsigned int iProjAngles, unsigned int iProjU, unsigned int iProjV, const SConeProjection* projs) { if (pData->initialized) return false; pData->dims.iProjAngles = iProjAngles; pData->dims.iProjU = iProjU; pData->dims.iProjV = iProjV; if (iProjAngles == 0 || iProjU == 0 || iProjV == 0 || projs == 0) return false; pData->projs = new SConeProjection[iProjAngles]; memcpy(pData->projs, projs, iProjAngles * sizeof(projs[0])); pData->projType = PROJ_CONE; return true; } bool AstraCGLS3d::setConeGeometry(unsigned int iProjAngles, unsigned int iProjU, unsigned int iProjV, float fOriginSourceDistance, float fOriginDetectorDistance, float fDetUSize, float fDetVSize, const float *pfAngles) { if (pData->initialized) return false; if (iProjAngles == 0 || iProjU == 0 || iProjV == 0 || pfAngles == 0) return false; SConeProjection* p = genConeProjections(iProjAngles, iProjU, iProjV, fOriginSourceDistance, fOriginDetectorDistance, fDetUSize, fDetVSize, pfAngles); pData->dims.iProjAngles = iProjAngles; pData->dims.iProjU = iProjU; pData->dims.iProjV = iProjV; pData->projs = p; pData->projType = PROJ_CONE; return true; } bool AstraCGLS3d::enableSuperSampling(unsigned int iVoxelSuperSampling, unsigned int iDetectorSuperSampling) { if (pData->initialized) return false; if (iVoxelSuperSampling == 0 || iDetectorSuperSampling == 0) return false; pData->dims.iRaysPerVoxelDim = iVoxelSuperSampling; pData->dims.iRaysPerDetDim = iDetectorSuperSampling; return true; } bool AstraCGLS3d::enableVolumeMask() { if (pData->initialized) return false; bool ok = pData->cgls.enableVolumeMask(); pData->useVolumeMask = ok; return ok; } #if 0 bool AstraCGLS3d::enableSinogramMask() { if (pData->initialized) return false; bool ok = pData->cgls.enableSinogramMask(); pData->useSinogramMask = ok; return ok; } #endif bool AstraCGLS3d::setGPUIndex(int index) { if (index != -1) { cudaSetDevice(index); cudaError_t err = cudaGetLastError(); // Ignore errors caused by calling cudaSetDevice multiple times if (err != cudaSuccess && err != cudaErrorSetOnActiveProcess) return false; } return true; } bool AstraCGLS3d::init() { if (pData->initialized) return false; if (pData->dims.iVolX == 0 || pData->dims.iProjAngles == 0) return false; bool ok; if (pData->projType == PROJ_PARALLEL) { ok = pData->cgls.setPar3DGeometry(pData->dims, pData->parprojs); } else { ok = pData->cgls.setConeGeometry(pData->dims, pData->projs); } if (!ok) return false; ok = pData->cgls.init(); if (!ok) return false; pData->D_volumeData = allocateVolumeData(pData->dims); ok = pData->D_volumeData.ptr; if (!ok) return false; pData->D_projData = allocateProjectionData(pData->dims); ok = pData->D_projData.ptr; if (!ok) { cudaFree(pData->D_volumeData.ptr); pData->D_volumeData.ptr = 0; return false; } if (pData->useVolumeMask) { pData->D_maskData = allocateVolumeData(pData->dims); ok = pData->D_maskData.ptr; if (!ok) { cudaFree(pData->D_volumeData.ptr); cudaFree(pData->D_projData.ptr); pData->D_volumeData.ptr = 0; pData->D_projData.ptr = 0; return false; } } if (pData->useSinogramMask) { pData->D_smaskData = allocateProjectionData(pData->dims); ok = pData->D_smaskData.ptr; if (!ok) { cudaFree(pData->D_volumeData.ptr); cudaFree(pData->D_projData.ptr); cudaFree(pData->D_maskData.ptr); pData->D_volumeData.ptr = 0; pData->D_projData.ptr = 0; pData->D_maskData.ptr = 0; return false; } } pData->initialized = true; return true; } #if 0 bool AstraCGLS3d::setMinConstraint(float fMin) { if (!pData->initialized) return false; return pData->cgls.setMinConstraint(fMin); } bool AstraCGLS3d::setMaxConstraint(float fMax) { if (!pData->initialized) return false; return pData->cgls.setMaxConstraint(fMax); } #endif bool AstraCGLS3d::setSinogram(const float* pfSinogram, unsigned int iSinogramPitch) { if (!pData->initialized) return false; if (!pfSinogram) return false; bool ok = copyProjectionsToDevice(pfSinogram, pData->D_projData, pData->dims, iSinogramPitch); if (!ok) return false; ok = pData->cgls.setBuffers(pData->D_volumeData, pData->D_projData); if (!ok) return false; pData->setStartReconstruction = false; return true; } bool AstraCGLS3d::setVolumeMask(const float* pfMask, unsigned int iMaskPitch) { if (!pData->initialized) return false; if (!pData->useVolumeMask) return false; if (!pfMask) return false; bool ok = copyVolumeToDevice(pfMask, pData->D_maskData, pData->dims, iMaskPitch); if (!ok) return false; ok = pData->cgls.setVolumeMask(pData->D_maskData); if (!ok) return false; return true; } #if 0 bool AstraCGLS3d::setSinogramMask(const float* pfMask, unsigned int iMaskPitch) { if (!pData->initialized) return false; if (!pData->useSinogramMask) return false; if (!pfMask) return false; bool ok = copyProjectionsToDevice(pfMask, pData->D_smaskData, pData->dims, iMaskPitch); if (!ok) return false; ok = pData->cgls.setSinogramMask(pData->D_smaskData); if (!ok) return false; return true; } #endif bool AstraCGLS3d::setStartReconstruction(const float* pfReconstruction, unsigned int iReconstructionPitch) { if (!pData->initialized) return false; if (!pfReconstruction) return false; bool ok = copyVolumeToDevice(pfReconstruction, pData->D_volumeData, pData->dims, iReconstructionPitch); if (!ok) return false; pData->setStartReconstruction = true; return true; } bool AstraCGLS3d::iterate(unsigned int iIterations) { if (!pData->initialized) return false; if (!pData->setStartReconstruction) zeroVolumeData(pData->D_volumeData, pData->dims); bool ok = pData->cgls.iterate(iIterations); if (!ok) return false; return true; } bool AstraCGLS3d::getReconstruction(float* pfReconstruction, unsigned int iReconstructionPitch) const { if (!pData->initialized) return false; bool ok = copyVolumeFromDevice(pfReconstruction, pData->D_volumeData, pData->dims, iReconstructionPitch); if (!ok) return false; return true; } void AstraCGLS3d::signalAbort() { if (!pData->initialized) return; pData->cgls.signalAbort(); } float AstraCGLS3d::computeDiffNorm() { if (!pData->initialized) return 0.0f; // FIXME: Error? return pData->cgls.computeDiffNorm(); } bool astraCudaConeFP(const float* pfVolume, float* pfProjections, unsigned int iVolX, unsigned int iVolY, unsigned int iVolZ, unsigned int iProjAngles, unsigned int iProjU, unsigned int iProjV, float fOriginSourceDistance, float fOriginDetectorDistance, float fDetUSize, float fDetVSize, const float *pfAngles, int iGPUIndex, int iDetectorSuperSampling) { if (iVolX == 0 || iVolY == 0 || iVolZ == 0) return false; if (iProjAngles == 0 || iProjU == 0 || iProjV == 0 || pfAngles == 0) return false; SConeProjection* p = genConeProjections(iProjAngles, iProjU, iProjV, fOriginSourceDistance, fOriginDetectorDistance, fDetUSize, fDetVSize, pfAngles); bool ok; ok = astraCudaConeFP(pfVolume, pfProjections, iVolX, iVolY, iVolZ, iProjAngles, iProjU, iProjV, p, iGPUIndex, iDetectorSuperSampling); delete[] p; return ok; } bool astraCudaConeFP(const float* pfVolume, float* pfProjections, unsigned int iVolX, unsigned int iVolY, unsigned int iVolZ, unsigned int iProjAngles, unsigned int iProjU, unsigned int iProjV, const SConeProjection *pfAngles, int iGPUIndex, int iDetectorSuperSampling) { SDimensions3D dims; dims.iVolX = iVolX; dims.iVolY = iVolY; dims.iVolZ = iVolZ; if (iVolX == 0 || iVolY == 0 || iVolZ == 0) return false; dims.iProjAngles = iProjAngles; dims.iProjU = iProjU; dims.iProjV = iProjV; if (iProjAngles == 0 || iProjU == 0 || iProjV == 0 || pfAngles == 0) return false; dims.iRaysPerDetDim = iDetectorSuperSampling; if (iDetectorSuperSampling == 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; } cudaPitchedPtr D_volumeData = allocateVolumeData(dims); bool ok = D_volumeData.ptr; if (!ok) return false; cudaPitchedPtr D_projData = allocateProjectionData(dims); ok = D_projData.ptr; if (!ok) { cudaFree(D_volumeData.ptr); return false; } ok &= copyVolumeToDevice(pfVolume, D_volumeData, dims, dims.iVolX); ok &= zeroProjectionData(D_projData, dims); if (!ok) { cudaFree(D_volumeData.ptr); cudaFree(D_projData.ptr); return false; } ok &= ConeFP(D_volumeData, D_projData, dims, pfAngles, 1.0f); ok &= copyProjectionsFromDevice(pfProjections, D_projData, dims, dims.iProjU); cudaFree(D_volumeData.ptr); cudaFree(D_projData.ptr); return ok; } bool astraCudaPar3DFP(const float* pfVolume, float* pfProjections, unsigned int iVolX, unsigned int iVolY, unsigned int iVolZ, unsigned int iProjAngles, unsigned int iProjU, unsigned int iProjV, float fDetUSize, float fDetVSize, const float *pfAngles, int iGPUIndex, int iDetectorSuperSampling, Cuda3DProjectionKernel projKernel) { if (iVolX == 0 || iVolY == 0 || iVolZ == 0) return false; if (iProjAngles == 0 || iProjU == 0 || iProjV == 0 || pfAngles == 0) return false; SPar3DProjection* p = genPar3DProjections(iProjAngles, iProjU, iProjV, fDetUSize, fDetVSize, pfAngles); bool ok; ok = astraCudaPar3DFP(pfVolume, pfProjections, iVolX, iVolY, iVolZ, iProjAngles, iProjU, iProjV, p, iGPUIndex, iDetectorSuperSampling, projKernel); delete[] p; return ok; } bool astraCudaPar3DFP(const float* pfVolume, float* pfProjections, unsigned int iVolX, unsigned int iVolY, unsigned int iVolZ, unsigned int iProjAngles, unsigned int iProjU, unsigned int iProjV, const SPar3DProjection *pfAngles, int iGPUIndex, int iDetectorSuperSampling, Cuda3DProjectionKernel projKernel) { SDimensions3D dims; dims.iVolX = iVolX; dims.iVolY = iVolY; dims.iVolZ = iVolZ; if (iVolX == 0 || iVolY == 0 || iVolZ == 0) return false; dims.iProjAngles = iProjAngles; dims.iProjU = iProjU; dims.iProjV = iProjV; if (iProjAngles == 0 || iProjU == 0 || iProjV == 0 || pfAngles == 0) return false; dims.iRaysPerDetDim = iDetectorSuperSampling; if (iDetectorSuperSampling == 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; } cudaPitchedPtr D_volumeData = allocateVolumeData(dims); bool ok = D_volumeData.ptr; if (!ok) return false; cudaPitchedPtr D_projData = allocateProjectionData(dims); ok = D_projData.ptr; if (!ok) { cudaFree(D_volumeData.ptr); return false; } ok &= copyVolumeToDevice(pfVolume, D_volumeData, dims, dims.iVolX); ok &= zeroProjectionData(D_projData, dims); if (!ok) { cudaFree(D_volumeData.ptr); cudaFree(D_projData.ptr); return false; } switch (projKernel) { case ker3d_default: ok &= Par3DFP(D_volumeData, D_projData, dims, pfAngles, 1.0f); break; case ker3d_sum_square_weights: ok &= Par3DFP_SumSqW(D_volumeData, D_projData, dims, pfAngles, 1.0f); break; default: assert(false); } ok &= copyProjectionsFromDevice(pfProjections, D_projData, dims, dims.iProjU); cudaFree(D_volumeData.ptr); cudaFree(D_projData.ptr); return ok; } bool astraCudaConeBP(float* pfVolume, const float* pfProjections, unsigned int iVolX, unsigned int iVolY, unsigned int iVolZ, unsigned int iProjAngles, unsigned int iProjU, unsigned int iProjV, float fOriginSourceDistance, float fOriginDetectorDistance, float fDetUSize, float fDetVSize, const float *pfAngles, int iGPUIndex, int iVoxelSuperSampling) { if (iVolX == 0 || iVolY == 0 || iVolZ == 0) return false; if (iProjAngles == 0 || iProjU == 0 || iProjV == 0 || pfAngles == 0) return false; SConeProjection* p = genConeProjections(iProjAngles, iProjU, iProjV, fOriginSourceDistance, fOriginDetectorDistance, fDetUSize, fDetVSize, pfAngles); bool ok; ok = astraCudaConeBP(pfVolume, pfProjections, iVolX, iVolY, iVolZ, iProjAngles, iProjU, iProjV, p, iGPUIndex, iVoxelSuperSampling); delete[] p; return ok; } bool astraCudaConeBP(float* pfVolume, const float* pfProjections, unsigned int iVolX, unsigned int iVolY, unsigned int iVolZ, unsigned int iProjAngles, unsigned int iProjU, unsigned int iProjV, const SConeProjection *pfAngles, int iGPUIndex, int iVoxelSuperSampling) { SDimensions3D dims; dims.iVolX = iVolX; dims.iVolY = iVolY; dims.iVolZ = iVolZ; if (iVolX == 0 || iVolY == 0 || iVolZ == 0) return false; dims.iProjAngles = iProjAngles; dims.iProjU = iProjU; dims.iProjV = iProjV; if (iProjAngles == 0 || iProjU == 0 || iProjV == 0 || pfAngles == 0) return false; dims.iRaysPerVoxelDim = iVoxelSuperSampling; if (iProjAngles == 0 || iProjU == 0 || iProjV == 0 || pfAngles == 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; } cudaPitchedPtr D_volumeData = allocateVolumeData(dims); bool ok = D_volumeData.ptr; if (!ok) return false; cudaPitchedPtr D_projData = allocateProjectionData(dims); ok = D_projData.ptr; if (!ok) { cudaFree(D_volumeData.ptr); return false; } ok &= copyProjectionsToDevice(pfProjections, D_projData, dims, dims.iProjU); ok &= zeroVolumeData(D_volumeData, dims); if (!ok) { cudaFree(D_volumeData.ptr); cudaFree(D_projData.ptr); return false; } ok &= ConeBP(D_volumeData, D_projData, dims, pfAngles); ok &= copyVolumeFromDevice(pfVolume, D_volumeData, dims, dims.iVolX); cudaFree(D_volumeData.ptr); cudaFree(D_projData.ptr); return ok; } bool astraCudaPar3DBP(float* pfVolume, const float* pfProjections, unsigned int iVolX, unsigned int iVolY, unsigned int iVolZ, unsigned int iProjAngles, unsigned int iProjU, unsigned int iProjV, float fDetUSize, float fDetVSize, const float *pfAngles, int iGPUIndex, int iVoxelSuperSampling) { if (iVolX == 0 || iVolY == 0 || iVolZ == 0) return false; if (iProjAngles == 0 || iProjU == 0 || iProjV == 0 || pfAngles == 0) return false; SPar3DProjection* p = genPar3DProjections(iProjAngles, iProjU, iProjV, fDetUSize, fDetVSize, pfAngles); bool ok; ok = astraCudaPar3DBP(pfVolume, pfProjections, iVolX, iVolY, iVolZ, iProjAngles, iProjU, iProjV, p, iGPUIndex, iVoxelSuperSampling); delete[] p; return ok; } // This computes the column weights, divides by them, and adds the // result to the current volume. This is both more expensive and more // GPU memory intensive than the regular BP, but allows saving system RAM. bool astraCudaPar3DBP_SIRTWeighted(float* pfVolume, const float* pfProjections, unsigned int iVolX, unsigned int iVolY, unsigned int iVolZ, unsigned int iProjAngles, unsigned int iProjU, unsigned int iProjV, float fDetUSize, float fDetVSize, const float *pfAngles, int iGPUIndex, int iVoxelSuperSampling) { if (iVolX == 0 || iVolY == 0 || iVolZ == 0) return false; if (iProjAngles == 0 || iProjU == 0 || iProjV == 0 || pfAngles == 0) return false; SPar3DProjection* p = genPar3DProjections(iProjAngles, iProjU, iProjV, fDetUSize, fDetVSize, pfAngles); bool ok; ok = astraCudaPar3DBP_SIRTWeighted(pfVolume, pfProjections, iVolX, iVolY, iVolZ, iProjAngles, iProjU, iProjV, p, iGPUIndex, iVoxelSuperSampling); delete[] p; return ok; } bool astraCudaPar3DBP(float* pfVolume, const float* pfProjections, unsigned int iVolX, unsigned int iVolY, unsigned int iVolZ, unsigned int iProjAngles, unsigned int iProjU, unsigned int iProjV, const SPar3DProjection *pfAngles, int iGPUIndex, int iVoxelSuperSampling) { SDimensions3D dims; dims.iVolX = iVolX; dims.iVolY = iVolY; dims.iVolZ = iVolZ; if (iVolX == 0 || iVolY == 0 || iVolZ == 0) return false; dims.iProjAngles = iProjAngles; dims.iProjU = iProjU; dims.iProjV = iProjV; if (iProjAngles == 0 || iProjU == 0 || iProjV == 0 || pfAngles == 0) return false; dims.iRaysPerVoxelDim = iVoxelSuperSampling; if (iGPUIndex != -1) { cudaSetDevice(iGPUIndex); cudaError_t err = cudaGetLastError(); // Ignore errors caused by calling cudaSetDevice multiple times if (err != cudaSuccess && err != cudaErrorSetOnActiveProcess) return false; } cudaPitchedPtr D_volumeData = allocateVolumeData(dims); bool ok = D_volumeData.ptr; if (!ok) return false; cudaPitchedPtr D_projData = allocateProjectionData(dims); ok = D_projData.ptr; if (!ok) { cudaFree(D_volumeData.ptr); return false; } ok &= copyProjectionsToDevice(pfProjections, D_projData, dims, dims.iProjU); ok &= zeroVolumeData(D_volumeData, dims); if (!ok) { cudaFree(D_volumeData.ptr); cudaFree(D_projData.ptr); return false; } ok &= Par3DBP(D_volumeData, D_projData, dims, pfAngles); ok &= copyVolumeFromDevice(pfVolume, D_volumeData, dims, dims.iVolX); cudaFree(D_volumeData.ptr); cudaFree(D_projData.ptr); return ok; } // This computes the column weights, divides by them, and adds the // result to the current volume. This is both more expensive and more // GPU memory intensive than the regular BP, but allows saving system RAM. bool astraCudaPar3DBP_SIRTWeighted(float* pfVolume, const float* pfProjections, unsigned int iVolX, unsigned int iVolY, unsigned int iVolZ, unsigned int iProjAngles, unsigned int iProjU, unsigned int iProjV, const SPar3DProjection *pfAngles, int iGPUIndex, int iVoxelSuperSampling) { SDimensions3D dims; dims.iVolX = iVolX; dims.iVolY = iVolY; dims.iVolZ = iVolZ; if (iVolX == 0 || iVolY == 0 || iVolZ == 0) return false; dims.iProjAngles = iProjAngles; dims.iProjU = iProjU; dims.iProjV = iProjV; if (iProjAngles == 0 || iProjU == 0 || iProjV == 0 || pfAngles == 0) return false; dims.iRaysPerVoxelDim = iVoxelSuperSampling; if (iGPUIndex != -1) { cudaSetDevice(iGPUIndex); cudaError_t err = cudaGetLastError(); // Ignore errors caused by calling cudaSetDevice multiple times if (err != cudaSuccess && err != cudaErrorSetOnActiveProcess) return false; } cudaPitchedPtr D_pixelWeight = allocateVolumeData(dims); bool ok = D_pixelWeight.ptr; if (!ok) return false; cudaPitchedPtr D_volumeData = allocateVolumeData(dims); ok = D_volumeData.ptr; if (!ok) { cudaFree(D_pixelWeight.ptr); return false; } cudaPitchedPtr D_projData = allocateProjectionData(dims); ok = D_projData.ptr; if (!ok) { cudaFree(D_pixelWeight.ptr); cudaFree(D_volumeData.ptr); return false; } // Compute weights ok &= zeroVolumeData(D_pixelWeight, dims); processSino3D(D_projData, 1.0f, dims); ok &= Par3DBP(D_pixelWeight, D_projData, dims, pfAngles); processVol3D(D_pixelWeight, dims); if (!ok) { cudaFree(D_pixelWeight.ptr); cudaFree(D_volumeData.ptr); cudaFree(D_projData.ptr); return false; } ok &= copyProjectionsToDevice(pfProjections, D_projData, dims, dims.iProjU); ok &= zeroVolumeData(D_volumeData, dims); // Do BP into D_volumeData ok &= Par3DBP(D_volumeData, D_projData, dims, pfAngles); // Multiply with weights processVol3D(D_volumeData, D_pixelWeight, dims); // Upload previous iterate to D_pixelWeight... ok &= copyVolumeToDevice(pfVolume, D_pixelWeight, dims, dims.iVolX); if (!ok) { cudaFree(D_pixelWeight.ptr); cudaFree(D_volumeData.ptr); cudaFree(D_projData.ptr); return false; } // ...and add it to the weighted BP processVol3D(D_volumeData, D_pixelWeight, dims); // Then copy the result back ok &= copyVolumeFromDevice(pfVolume, D_volumeData, dims, dims.iVolX); cudaFree(D_pixelWeight.ptr); cudaFree(D_volumeData.ptr); cudaFree(D_projData.ptr); return ok; } bool astraCudaFDK(float* pfVolume, const float* pfProjections, unsigned int iVolX, unsigned int iVolY, unsigned int iVolZ, unsigned int iProjAngles, unsigned int iProjU, unsigned int iProjV, float fOriginSourceDistance, float fOriginDetectorDistance, float fDetUSize, float fDetVSize, const float *pfAngles, bool bShortScan, int iGPUIndex, int iVoxelSuperSampling) { SDimensions3D dims; dims.iVolX = iVolX; dims.iVolY = iVolY; dims.iVolZ = iVolZ; if (iVolX == 0 || iVolY == 0 || iVolZ == 0) return false; dims.iProjAngles = iProjAngles; dims.iProjU = iProjU; dims.iProjV = iProjV; if (iProjAngles == 0 || iProjU == 0 || iProjV == 0 || pfAngles == 0) return false; dims.iRaysPerVoxelDim = iVoxelSuperSampling; if (iVoxelSuperSampling == 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; } cudaPitchedPtr D_volumeData = allocateVolumeData(dims); bool ok = D_volumeData.ptr; if (!ok) return false; cudaPitchedPtr D_projData = allocateProjectionData(dims); ok = D_projData.ptr; if (!ok) { cudaFree(D_volumeData.ptr); return false; } ok &= copyProjectionsToDevice(pfProjections, D_projData, dims, dims.iProjU); ok &= zeroVolumeData(D_volumeData, dims); if (!ok) { cudaFree(D_volumeData.ptr); cudaFree(D_projData.ptr); return false; } // TODO: Offer interface for SrcZ, DetZ ok &= FDK(D_volumeData, D_projData, fOriginSourceDistance, fOriginDetectorDistance, 0, 0, fDetUSize, fDetVSize, dims, pfAngles, bShortScan); ok &= copyVolumeFromDevice(pfVolume, D_volumeData, dims, dims.iVolX); cudaFree(D_volumeData.ptr); cudaFree(D_projData.ptr); return ok; } }