/* ----------------------------------------------------------------------- Copyright 2012 iMinds-Vision Lab, University of Antwerp Contact: astra@ua.ac.be Website: http://astra.ua.ac.be This file is part of the All Scale Tomographic Reconstruction Antwerp Toolbox ("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$ */ #ifdef ASTRA_CUDA #include "astra/CudaReconstructionAlgorithm2D.h" #include #include "astra/AstraObjectManager.h" #include "astra/FanFlatProjectionGeometry2D.h" #include "astra/FanFlatVecProjectionGeometry2D.h" #include "astra/CudaProjector2D.h" #include "../cuda/2d/algo.h" #include using namespace std; namespace astra { //---------------------------------------------------------------------------------------- // Constructor CCudaReconstructionAlgorithm2D::CCudaReconstructionAlgorithm2D() { _clear(); } //---------------------------------------------------------------------------------------- // Destructor CCudaReconstructionAlgorithm2D::~CCudaReconstructionAlgorithm2D() { delete m_pAlgo; m_pAlgo = 0; m_bAlgoInit = false; } void CCudaReconstructionAlgorithm2D::clear() { delete m_pAlgo; _clear(); } void CCudaReconstructionAlgorithm2D::_clear() { m_bIsInitialized = false; m_pAlgo = 0; m_bAlgoInit = false; CReconstructionAlgorithm2D::_clear(); m_iGPUIndex = 0; m_iDetectorSuperSampling = 1; m_iPixelSuperSampling = 1; } //--------------------------------------------------------------------------------------- // Initialize - Config bool CCudaReconstructionAlgorithm2D::initialize(const Config& _cfg) { ASTRA_ASSERT(_cfg.self); ConfigStackCheck CC("CudaReconstructionAlgorithm2D", this, _cfg); // if already initialized, clear first if (m_bIsInitialized) { clear(); } // sinogram data XMLNode* node = _cfg.self->getSingleNode("ProjectionDataId"); ASTRA_CONFIG_CHECK(node, "CudaSirt2", "No ProjectionDataId tag specified."); int id = boost::lexical_cast(node->getContent()); m_pSinogram = dynamic_cast(CData2DManager::getSingleton().get(id)); ASTRA_DELETE(node); CC.markNodeParsed("ProjectionDataId"); // reconstruction data node = _cfg.self->getSingleNode("ReconstructionDataId"); ASTRA_CONFIG_CHECK(node, "CudaSirt2", "No ReconstructionDataId tag specified."); id = boost::lexical_cast(node->getContent()); m_pReconstruction = dynamic_cast(CData2DManager::getSingleton().get(id)); ASTRA_DELETE(node); CC.markNodeParsed("ReconstructionDataId"); // fixed mask if (_cfg.self->hasOption("ReconstructionMaskId")) { m_bUseReconstructionMask = true; id = boost::lexical_cast(_cfg.self->getOption("ReconstructionMaskId")); m_pReconstructionMask = dynamic_cast(CData2DManager::getSingleton().get(id)); } CC.markOptionParsed("ReconstructionMaskId"); // fixed mask if (_cfg.self->hasOption("SinogramMaskId")) { m_bUseSinogramMask = true; id = boost::lexical_cast(_cfg.self->getOption("SinogramMaskId")); m_pSinogramMask = dynamic_cast(CData2DManager::getSingleton().get(id)); } CC.markOptionParsed("SinogramMaskId"); // Constraints - NEW if (_cfg.self->hasOption("MinConstraint")) { m_bUseMinConstraint = true; m_fMinValue = _cfg.self->getOptionNumerical("MinConstraint", 0.0f); CC.markOptionParsed("MinConstraint"); } else { // Constraint - OLD m_bUseMinConstraint = _cfg.self->getOptionBool("UseMinConstraint", false); CC.markOptionParsed("UseMinConstraint"); if (m_bUseMinConstraint) { m_fMinValue = _cfg.self->getOptionNumerical("MinConstraintValue", 0.0f); CC.markOptionParsed("MinConstraintValue"); } } if (_cfg.self->hasOption("MaxConstraint")) { m_bUseMaxConstraint = true; m_fMaxValue = _cfg.self->getOptionNumerical("MaxConstraint", 255.0f); CC.markOptionParsed("MaxConstraint"); } else { // Constraint - OLD m_bUseMaxConstraint = _cfg.self->getOptionBool("UseMaxConstraint", false); CC.markOptionParsed("UseMaxConstraint"); if (m_bUseMaxConstraint) { m_fMaxValue = _cfg.self->getOptionNumerical("MaxConstraintValue", 0.0f); CC.markOptionParsed("MaxConstraintValue"); } } // GPU number m_iGPUIndex = (int)_cfg.self->getOptionNumerical("GPUindex", 0); m_iGPUIndex = (int)_cfg.self->getOptionNumerical("GPUIndex", m_iGPUIndex); CC.markOptionParsed("GPUindex"); if (!_cfg.self->hasOption("GPUindex")) CC.markOptionParsed("GPUIndex"); // Detector supersampling factor m_iDetectorSuperSampling = (int)_cfg.self->getOptionNumerical("DetectorSuperSampling", 1); CC.markOptionParsed("DetectorSuperSampling"); // Pixel supersampling factor m_iPixelSuperSampling = (int)_cfg.self->getOptionNumerical("PixelSuperSampling", 1); CC.markOptionParsed("PixelSuperSampling"); // This isn't used yet, but passing it is not something to warn about node = _cfg.self->getSingleNode("ProjectorId"); if (node) { id = boost::lexical_cast(node->getContent()); CProjector2D *projector = CProjector2DManager::getSingleton().get(id); if (!dynamic_cast(projector)) { cout << "Warning: non-CUDA Projector2D passed to FP_CUDA" << std::endl; } delete node; } CC.markNodeParsed("ProjectorId"); return _check(); } //--------------------------------------------------------------------------------------- // Initialize - C++ bool CCudaReconstructionAlgorithm2D::initialize(CProjector2D* _pProjector, CFloat32ProjectionData2D* _pSinogram, CFloat32VolumeData2D* _pReconstruction) { return initialize(_pProjector, _pSinogram, _pReconstruction, 0, 1); } //--------------------------------------------------------------------------------------- // Initialize - C++ bool CCudaReconstructionAlgorithm2D::initialize(CProjector2D* _pProjector, CFloat32ProjectionData2D* _pSinogram, CFloat32VolumeData2D* _pReconstruction, int _iGPUindex, int _iDetectorSuperSampling, int _iPixelSuperSampling) { // if already initialized, clear first if (m_bIsInitialized) { clear(); } m_pProjector = 0; // required classes m_pSinogram = _pSinogram; m_pReconstruction = _pReconstruction; m_iDetectorSuperSampling = _iDetectorSuperSampling; m_iPixelSuperSampling = _iPixelSuperSampling; m_iGPUIndex = _iGPUindex; return _check(); } //---------------------------------------------------------------------------------------- // Check bool CCudaReconstructionAlgorithm2D::_check() { // TODO: CLEAN UP // check pointers //ASTRA_CONFIG_CHECK(m_pProjector, "Reconstruction2D", "Invalid Projector Object."); ASTRA_CONFIG_CHECK(m_pSinogram, "SIRT_CUDA", "Invalid Projection Data Object."); ASTRA_CONFIG_CHECK(m_pReconstruction, "SIRT_CUDA", "Invalid Reconstruction Data Object."); // check initializations //ASTRA_CONFIG_CHECK(m_pProjector->isInitialized(), "Reconstruction2D", "Projector Object Not Initialized."); ASTRA_CONFIG_CHECK(m_pSinogram->isInitialized(), "SIRT_CUDA", "Projection Data Object Not Initialized."); ASTRA_CONFIG_CHECK(m_pReconstruction->isInitialized(), "SIRT_CUDA", "Reconstruction Data Object Not Initialized."); ASTRA_CONFIG_CHECK(m_iDetectorSuperSampling >= 1, "SIRT_CUDA", "DetectorSuperSampling must be a positive integer."); ASTRA_CONFIG_CHECK(m_iPixelSuperSampling >= 1, "SIRT_CUDA", "PixelSuperSampling must be a positive integer."); ASTRA_CONFIG_CHECK(m_iGPUIndex >= 0, "SIRT_CUDA", "GPUIndex must be a non-negative integer."); // check compatibility between projector and data classes // ASTRA_CONFIG_CHECK(m_pSinogram->getGeometry()->isEqual(m_pProjector->getProjectionGeometry()), "SIRT_CUDA", "Projection Data not compatible with the specified Projector."); // ASTRA_CONFIG_CHECK(m_pReconstruction->getGeometry()->isEqual(m_pProjector->getVolumeGeometry()), "SIRT_CUDA", "Reconstruction Data not compatible with the specified Projector."); // todo: turn some of these back on // ASTRA_CONFIG_CHECK(m_pProjectionGeometry, "SIRT_CUDA", "ProjectionGeometry not specified."); // ASTRA_CONFIG_CHECK(m_pProjectionGeometry->isInitialized(), "SIRT_CUDA", "ProjectionGeometry not initialized."); // ASTRA_CONFIG_CHECK(m_pReconstructionGeometry, "SIRT_CUDA", "ReconstructionGeometry not specified."); // ASTRA_CONFIG_CHECK(m_pReconstructionGeometry->isInitialized(), "SIRT_CUDA", "ReconstructionGeometry not initialized."); // check dimensions //ASTRA_CONFIG_CHECK(m_pSinogram->getAngleCount() == m_pProjectionGeometry->getProjectionAngleCount(), "SIRT_CUDA", "Sinogram data object size mismatch."); //ASTRA_CONFIG_CHECK(m_pSinogram->getDetectorCount() == m_pProjectionGeometry->getDetectorCount(), "SIRT_CUDA", "Sinogram data object size mismatch."); //ASTRA_CONFIG_CHECK(m_pReconstruction->getWidth() == m_pReconstructionGeometry->getGridColCount(), "SIRT_CUDA", "Reconstruction data object size mismatch."); //ASTRA_CONFIG_CHECK(m_pReconstruction->getHeight() == m_pReconstructionGeometry->getGridRowCount(), "SIRT_CUDA", "Reconstruction data object size mismatch."); // check restrictions // TODO: check restrictions built into cuda code // success m_bIsInitialized = true; return true; } void CCudaReconstructionAlgorithm2D::setGPUIndex(int _iGPUIndex) { m_iGPUIndex = _iGPUIndex; } //--------------------------------------------------------------------------------------- // Information - All map CCudaReconstructionAlgorithm2D::getInformation() { // TODO: Verify and clean up map res; res["ProjectionGeometry"] = getInformation("ProjectionGeometry"); res["ReconstructionGeometry"] = getInformation("ReconstructionGeometry"); res["ProjectionDataId"] = getInformation("ProjectionDataId"); res["ReconstructionDataId"] = getInformation("ReconstructionDataId"); res["ReconstructionMaskId"] = getInformation("ReconstructionMaskId"); res["GPUindex"] = getInformation("GPUindex"); res["DetectorSuperSampling"] = getInformation("DetectorSuperSampling"); res["PixelSuperSampling"] = getInformation("PixelSuperSampling"); res["UseMinConstraint"] = getInformation("UseMinConstraint"); res["MinConstraintValue"] = getInformation("MinConstraintValue"); res["UseMaxConstraint"] = getInformation("UseMaxConstraint"); res["MaxConstraintValue"] = getInformation("MaxConstraintValue"); return mergeMap(CReconstructionAlgorithm2D::getInformation(), res); } //--------------------------------------------------------------------------------------- // Information - Specific boost::any CCudaReconstructionAlgorithm2D::getInformation(std::string _sIdentifier) { // TODO: Verify and clean up if (_sIdentifier == "UseMinConstraint") { return m_bUseMinConstraint ? string("yes") : string("no"); } if (_sIdentifier == "MinConstraintValue") { return m_fMinValue; } if (_sIdentifier == "UseMaxConstraint") { return m_bUseMaxConstraint ? string("yes") : string("no"); } if (_sIdentifier == "MaxConstraintValue") { return m_fMaxValue; } // TODO: store these so we can return them? if (_sIdentifier == "ProjectionGeometry") { return string("not implemented"); } if (_sIdentifier == "ReconstructionGeometry") { return string("not implemented"); } if (_sIdentifier == "GPUindex") { return m_iGPUIndex; } if (_sIdentifier == "DetectorSuperSampling") { return m_iDetectorSuperSampling; } if (_sIdentifier == "PixelSuperSampling") { return m_iPixelSuperSampling; } if (_sIdentifier == "ProjectionDataId") { int iIndex = CData2DManager::getSingleton().getIndex(m_pSinogram); if (iIndex != 0) return iIndex; return std::string("not in manager"); } if (_sIdentifier == "ReconstructionDataId") { int iIndex = CData2DManager::getSingleton().getIndex(m_pReconstruction); if (iIndex != 0) return iIndex; return std::string("not in manager"); } if (_sIdentifier == "ReconstructionMaskId") { if (!m_bUseReconstructionMask) return string("not used"); int iIndex = CData2DManager::getSingleton().getIndex(m_pReconstructionMask); if (iIndex != 0) return iIndex; return std::string("not in manager"); } return CReconstructionAlgorithm2D::getInformation(_sIdentifier); } bool CCudaReconstructionAlgorithm2D::setupGeometry() { ASTRA_ASSERT(m_bIsInitialized); ASTRA_ASSERT(!m_bAlgoInit); bool ok; // TODO: Probably not the best place for this... ok = m_pAlgo->setGPUIndex(m_iGPUIndex); if (!ok) return false; astraCUDA::SDimensions dims; const CVolumeGeometry2D& volgeom = *m_pReconstruction->getGeometry(); // TODO: off-center geometry, 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(m_pSinogram->getGeometry()); const CFanFlatProjectionGeometry2D* fanProjGeom = dynamic_cast(m_pSinogram->getGeometry()); const CFanFlatVecProjectionGeometry2D* fanVecProjGeom = dynamic_cast(m_pSinogram->getGeometry()); if (parProjGeom) { dims.iProjAngles = parProjGeom->getProjectionAngleCount(); dims.iProjDets = parProjGeom->getDetectorCount(); dims.fDetScale = parProjGeom->getDetectorWidth() / fPixelSize; ok = m_pAlgo->setGeometry(dims, parProjGeom->getProjectionAngles()); } else if (fanProjGeom) { dims.iProjAngles = fanProjGeom->getProjectionAngleCount(); dims.iProjDets = fanProjGeom->getDetectorCount(); dims.fDetScale = fanProjGeom->getDetectorWidth() / fPixelSize; float fOriginSourceDistance = fanProjGeom->getOriginSourceDistance(); float fOriginDetectorDistance = fanProjGeom->getOriginDetectorDistance(); const float* angles = fanProjGeom->getProjectionAngles(); astraCUDA::SFanProjection* projs; projs = new astraCUDA::SFanProjection[dims.iProjAngles]; float fSrcX0 = 0.0f; float fSrcY0 = -fOriginSourceDistance / fPixelSize; float fDetUX0 = dims.fDetScale; float fDetUY0 = 0.0f; float fDetSX0 = dims.iProjDets * fDetUX0 / -2.0f; float fDetSY0 = fOriginDetectorDistance / fPixelSize; #define ROTATE0(name,i,alpha) do { projs[i].f##name##X = f##name##X0 * cos(alpha) - f##name##Y0 * sin(alpha); projs[i].f##name##Y = f##name##X0 * sin(alpha) + f##name##Y0 * cos(alpha); } while(0) for (unsigned int i = 0; i < dims.iProjAngles; ++i) { ROTATE0(Src, i, angles[i]); ROTATE0(DetS, i, angles[i]); ROTATE0(DetU, i, angles[i]); } #undef ROTATE0 ok = m_pAlgo->setFanGeometry(dims, projs); delete[] projs; } else if (fanVecProjGeom) { dims.iProjAngles = fanVecProjGeom->getProjectionAngleCount(); dims.iProjDets = fanVecProjGeom->getDetectorCount(); dims.fDetScale = fanVecProjGeom->getDetectorWidth() / fPixelSize; const astraCUDA::SFanProjection* projs; projs = fanVecProjGeom->getProjectionVectors(); // Rescale projs to fPixelSize == 1 astraCUDA::SFanProjection* scaledProjs = new astraCUDA::SFanProjection[dims.iProjAngles]; #define SCALE(name,i,alpha) do { scaledProjs[i].f##name##X = projs[i].f##name##X * alpha; scaledProjs[i].f##name##Y = projs[i].f##name##Y * alpha; } while (0) for (unsigned int i = 0; i < dims.iProjAngles; ++i) { SCALE(Src,i,1.0f/fPixelSize); SCALE(DetS,i,1.0f/fPixelSize); SCALE(DetU,i,1.0f/fPixelSize); } ok = m_pAlgo->setFanGeometry(dims, scaledProjs); delete[] scaledProjs; } else { ASTRA_ASSERT(false); } if (!ok) return false; if (m_bUseReconstructionMask) ok &= m_pAlgo->enableVolumeMask(); if (!ok) return false; if (m_bUseSinogramMask) ok &= m_pAlgo->enableSinogramMask(); if (!ok) return false; const float *pfTOffsets = m_pSinogram->getGeometry()->getExtraDetectorOffset(); if (pfTOffsets) ok &= m_pAlgo->setTOffsets(pfTOffsets); if (!ok) return false; ok &= m_pAlgo->init(); if (!ok) return false; return true; } //---------------------------------------------------------------------------------------- // Iterate void CCudaReconstructionAlgorithm2D::run(int _iNrIterations) { // check initialized ASTRA_ASSERT(m_bIsInitialized); bool ok = true; const CVolumeGeometry2D& volgeom = *m_pReconstruction->getGeometry(); if (!m_bAlgoInit) { ok = setupGeometry(); ASTRA_ASSERT(ok); ok = m_pAlgo->allocateBuffers(); ASTRA_ASSERT(ok); m_bAlgoInit = true; } float fPixelSize = volgeom.getPixelLengthX(); float fSinogramScale = 1.0f/(fPixelSize*fPixelSize); ok = m_pAlgo->copyDataToGPU(m_pSinogram->getDataConst(), m_pSinogram->getGeometry()->getDetectorCount(), fSinogramScale, m_pReconstruction->getDataConst(), volgeom.getGridColCount(), m_bUseReconstructionMask ? m_pReconstructionMask->getDataConst() : 0, volgeom.getGridColCount(), m_bUseSinogramMask ? m_pSinogramMask->getDataConst() : 0, m_pSinogram->getGeometry()->getDetectorCount()); ASTRA_ASSERT(ok); if (m_bUseMinConstraint) ok &= m_pAlgo->setMinConstraint(m_fMinValue); if (m_bUseMaxConstraint) ok &= m_pAlgo->setMaxConstraint(m_fMaxValue); ok &= m_pAlgo->iterate(_iNrIterations); ASTRA_ASSERT(ok); ok &= m_pAlgo->getReconstruction(m_pReconstruction->getData(), volgeom.getGridColCount()); ASTRA_ASSERT(ok); } void CCudaReconstructionAlgorithm2D::signalAbort() { if (m_bIsInitialized && m_pAlgo) { m_pAlgo->signalAbort(); } } bool CCudaReconstructionAlgorithm2D::getResidualNorm(float32& _fNorm) { if (!m_bIsInitialized || !m_pAlgo) return false; _fNorm = m_pAlgo->computeDiffNorm(); return true; } } // namespace astra #endif // ASTRA_CUDA