/* ----------------------------------------------------------------------- Copyright: 2010-2018, iMinds-Vision Lab, University of Antwerp 2014-2018, CWI, Amsterdam Contact: astra@astra-toolbox.com Website: http://www.astra-toolbox.com/ This file is part of the ASTRA Toolbox. The ASTRA Toolbox is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. The ASTRA Toolbox is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with the ASTRA Toolbox. If not, see . ----------------------------------------------------------------------- */ #include "astra/ArtAlgorithm.h" #include "astra/AstraObjectManager.h" using namespace std; namespace astra { // type of the algorithm, needed to register with CAlgorithmFactory std::string CArtAlgorithm::type = "ART"; //---------------------------------------------------------------------------------------- // Constructor CArtAlgorithm::CArtAlgorithm() : CReconstructionAlgorithm2D() { m_fLambda = 1.0f; m_iRayCount = 0; m_iCurrentRay = 0; m_piProjectionOrder = NULL; m_piDetectorOrder = NULL; m_bIsInitialized = false; } //---------------------------------------------------------------------------------------- // Destructor CArtAlgorithm::~CArtAlgorithm() { if (m_piProjectionOrder != NULL) delete[] m_piProjectionOrder; if (m_piDetectorOrder != NULL) delete[] m_piDetectorOrder; } //--------------------------------------------------------------------------------------- // Clear - Constructors void CArtAlgorithm::_clear() { CReconstructionAlgorithm2D::_clear(); m_piDetectorOrder = NULL; m_piProjectionOrder = NULL; m_iRayCount = 0; m_iCurrentRay = 0; m_bIsInitialized = false; } //--------------------------------------------------------------------------------------- // Clear - Public void CArtAlgorithm::clear() { CReconstructionAlgorithm2D::clear(); if (m_piDetectorOrder) { delete[] m_piDetectorOrder; m_piDetectorOrder = NULL; } if (m_piProjectionOrder) { delete[] m_piProjectionOrder; m_piProjectionOrder = NULL; } m_fLambda = 1.0f; m_iRayCount = 0; m_iCurrentRay = 0; m_bIsInitialized = false; } //--------------------------------------------------------------------------------------- // Check bool CArtAlgorithm::_check() { // check base class ASTRA_CONFIG_CHECK(CReconstructionAlgorithm2D::_check(), "ART", "Error in ReconstructionAlgorithm2D initialization"); // check ray order list for (int i = 0; i < m_iRayCount; i++) { if (m_piProjectionOrder[i] < 0 || m_piProjectionOrder[i] > m_pSinogram->getAngleCount()-1) { ASTRA_CONFIG_CHECK(false, "ART", "Invalid value in ray order list."); } if (m_piDetectorOrder[i] < 0 || m_piDetectorOrder[i] > m_pSinogram->getDetectorCount()-1) { ASTRA_CONFIG_CHECK(false, "ART", "Invalid value in ray order list."); } } // success return true; } //--------------------------------------------------------------------------------------- // Initialize - Config bool CArtAlgorithm::initialize(const Config& _cfg) { ASTRA_ASSERT(_cfg.self); ConfigStackCheck CC("ArtAlgorithm", this, _cfg); // if already initialized, clear first if (m_bIsInitialized) { clear(); } // initialization of parent class if (!CReconstructionAlgorithm2D::initialize(_cfg)) { return false; } // ray order string projOrder = _cfg.self.getOption("RayOrder", "sequential"); CC.markOptionParsed("RayOrder"); m_iCurrentRay = 0; m_iRayCount = m_pProjector->getProjectionGeometry()->getProjectionAngleCount() * m_pProjector->getProjectionGeometry()->getDetectorCount(); if (projOrder == "sequential") { m_piProjectionOrder = new int[m_iRayCount]; m_piDetectorOrder = new int[m_iRayCount]; for (int i = 0; i < m_iRayCount; i++) { m_piProjectionOrder[i] = (int)floor((float)i / m_pProjector->getProjectionGeometry()->getDetectorCount()); m_piDetectorOrder[i] = i % m_pProjector->getProjectionGeometry()->getDetectorCount(); } } else if (projOrder == "custom") { vector rayOrderList = _cfg.self.getOptionNumericalArray("RayOrderList"); m_iRayCount = rayOrderList.size() / 2; m_piProjectionOrder = new int[m_iRayCount]; m_piDetectorOrder = new int[m_iRayCount]; for (int i = 0; i < m_iRayCount; i++) { m_piProjectionOrder[i] = static_cast(rayOrderList[2*i]); m_piDetectorOrder[i] = static_cast(rayOrderList[2*i+1]); } CC.markOptionParsed("RayOrderList"); } else { return false; } // "Lambda" is replaced by the more descriptive "Relaxation" m_fLambda = _cfg.self.getOptionNumerical("Lambda", 1.0f); m_fLambda = _cfg.self.getOptionNumerical("Relaxation", m_fLambda); if (!_cfg.self.hasOption("Relaxation")) CC.markOptionParsed("Lambda"); CC.markOptionParsed("Relaxation"); // success m_bIsInitialized = _check(); return m_bIsInitialized; } //---------------------------------------------------------------------------------------- // Initialize - C++ bool CArtAlgorithm::initialize(CProjector2D* _pProjector, CFloat32ProjectionData2D* _pSinogram, CFloat32VolumeData2D* _pReconstruction) { // if already initialized, clear first if (m_bIsInitialized) { clear(); } // required classes m_pProjector = _pProjector; m_pSinogram = _pSinogram; m_pReconstruction = _pReconstruction; // ray order m_iCurrentRay = 0; m_iRayCount = _pProjector->getProjectionGeometry()->getDetectorCount() * _pProjector->getProjectionGeometry()->getProjectionAngleCount(); m_piProjectionOrder = new int[m_iRayCount]; m_piDetectorOrder = new int[m_iRayCount]; for (int i = 0; i < m_iRayCount; i++) { m_piProjectionOrder[i] = (int)floor((float)i / _pProjector->getProjectionGeometry()->getDetectorCount()); m_piDetectorOrder[i] = i % _pProjector->getProjectionGeometry()->getDetectorCount(); } // success m_bIsInitialized = _check(); return m_bIsInitialized; } //---------------------------------------------------------------------------------------- // Set the relaxation factor. void CArtAlgorithm::setLambda(float32 _fLambda) { m_fLambda = _fLambda; } //---------------------------------------------------------------------------------------- // Set the order in which the rays will be selected void CArtAlgorithm::setRayOrder(int* _piProjectionOrder, int* _piDetectorOrder, int _iRayCount) { if (m_piDetectorOrder) { delete[] m_piDetectorOrder; m_piDetectorOrder = NULL; } if (m_piProjectionOrder) { delete[] m_piProjectionOrder; m_piProjectionOrder = NULL; } m_iCurrentRay = 0; m_iRayCount = _iRayCount; m_piProjectionOrder = new int[m_iRayCount]; m_piDetectorOrder = new int[m_iRayCount]; for (int i = 0; i < m_iRayCount; i++) { m_piProjectionOrder[i] = _piProjectionOrder[i]; m_piDetectorOrder[i] = _piDetectorOrder[i]; } } //--------------------------------------------------------------------------------------- // Information - All map CArtAlgorithm::getInformation() { map res; res["RayOrder"] = getInformation("RayOrder"); res["Relaxation"] = getInformation("Relaxation"); return mergeMap(CReconstructionAlgorithm2D::getInformation(), res); }; //--------------------------------------------------------------------------------------- // Information - Specific boost::any CArtAlgorithm::getInformation(std::string _sIdentifier) { if (_sIdentifier == "Relaxation") { return m_fLambda; } if (_sIdentifier == "RayOrder") { vector res; for (int i = 0; i < m_iRayCount; i++) { res.push_back(m_piProjectionOrder[i]); } for (int i = 0; i < m_iRayCount; i++) { res.push_back(m_piDetectorOrder[i]); } return res; } return CAlgorithm::getInformation(_sIdentifier); }; //---------------------------------------------------------------------------------------- // Iterate void CArtAlgorithm::run(int _iNrIterations) { // check initialized assert(m_bIsInitialized); // variables int iIteration, iPixel; int iUsedPixels, iProjection, iDetector; float32 fRayForwardProj, fSumSquaredWeights; float32 fProjectionDifference, fBackProjectionFactor; // create a pixel buffer int iPixelBufferSize = m_pProjector->getProjectionWeightsCount(0); SPixelWeight* pPixels = new SPixelWeight[iPixelBufferSize]; // start iterations for (iIteration = _iNrIterations-1; iIteration >= 0; --iIteration) { // step0: compute single weight rays iProjection = m_piProjectionOrder[m_iCurrentRay]; iDetector = m_piDetectorOrder[m_iCurrentRay]; m_iCurrentRay = (m_iCurrentRay + 1) % m_iRayCount; if (m_bUseSinogramMask && m_pSinogramMask->getData2D()[iProjection][iDetector] == 0) continue; m_pProjector->computeSingleRayWeights(iProjection, iDetector, pPixels, iPixelBufferSize, iUsedPixels); // step1: forward projections fRayForwardProj = 0.0f; fSumSquaredWeights = 0.0f; for (iPixel = iUsedPixels-1; iPixel >= 0; --iPixel) { if (m_bUseReconstructionMask && m_pReconstructionMask->getDataConst()[pPixels[iPixel].m_iIndex] == 0) continue; fRayForwardProj += pPixels[iPixel].m_fWeight * m_pReconstruction->getDataConst()[pPixels[iPixel].m_iIndex]; fSumSquaredWeights += pPixels[iPixel].m_fWeight * pPixels[iPixel].m_fWeight; } if (fSumSquaredWeights == 0) continue; // step2: difference fProjectionDifference = m_pSinogram->getData2D()[iProjection][iDetector] - fRayForwardProj; // step3: back projection fBackProjectionFactor = m_fLambda * fProjectionDifference / fSumSquaredWeights; for (iPixel = iUsedPixels-1; iPixel >= 0; --iPixel) { // pixel must be loose if (m_bUseReconstructionMask && m_pReconstructionMask->getDataConst()[pPixels[iPixel].m_iIndex] == 0) continue; // update m_pReconstruction->getData()[pPixels[iPixel].m_iIndex] += fBackProjectionFactor * pPixels[iPixel].m_fWeight; // constraints if (m_bUseMinConstraint && m_pReconstruction->getData()[pPixels[iPixel].m_iIndex] < m_fMinValue) { m_pReconstruction->getData()[pPixels[iPixel].m_iIndex] = m_fMinValue; } if (m_bUseMaxConstraint && m_pReconstruction->getData()[pPixels[iPixel].m_iIndex] > m_fMaxValue) { m_pReconstruction->getData()[pPixels[iPixel].m_iIndex] = m_fMaxValue; } } } delete[] pPixels; // update statistics m_pReconstruction->updateStatistics(); } //---------------------------------------------------------------------------------------- } // namespace astra