diff --git a/Framework/Algorithms/CMakeLists.txt b/Framework/Algorithms/CMakeLists.txt index 3a653bd17cbad0a5692b857deddd8863c572e721..217daf7a5c9e08a4125e223ed79229622f92002f 100644 --- a/Framework/Algorithms/CMakeLists.txt +++ b/Framework/Algorithms/CMakeLists.txt @@ -142,6 +142,7 @@ set ( SRC_FILES src/He3TubeEfficiency.cpp src/IQTransform.cpp src/IdentifyNoisyDetectors.cpp + src/ImggTomographicReconstruction.cpp src/IntegrateByComponent.cpp src/Integration.cpp src/InterpolatingRebin.cpp @@ -268,6 +269,7 @@ set ( SRC_FILES src/TimeAtSampleStrategyDirect.cpp src/TimeAtSampleStrategyElastic.cpp src/TimeAtSampleStrategyIndirect.cpp + src/Tomography/FBPTomopy.cpp src/Transpose.cpp src/UnGroupWorkspace.cpp src/UnaryOperation.cpp @@ -283,6 +285,12 @@ set ( SRC_FILES src/XDataConverter.cpp ) +# C as they originally come from a different project (tomopy) +set ( C_SRC_FILES + src/Tomography/tomopy/fbp.c + src/Tomography/tomopy/utils.c +) + set ( INC_FILES inc/MantidAlgorithms/AbsorptionCorrection.h inc/MantidAlgorithms/AddLogDerivative.h @@ -428,6 +436,7 @@ set ( INC_FILES inc/MantidAlgorithms/He3TubeEfficiency.h inc/MantidAlgorithms/IQTransform.h inc/MantidAlgorithms/IdentifyNoisyDetectors.h + inc/MantidAlgorithms/ImggTomographicReconstruction.h inc/MantidAlgorithms/IntegrateByComponent.h inc/MantidAlgorithms/Integration.h inc/MantidAlgorithms/InterpolatingRebin.h @@ -557,6 +566,9 @@ set ( INC_FILES inc/MantidAlgorithms/TimeAtSampleStrategyDirect.h inc/MantidAlgorithms/TimeAtSampleStrategyElastic.h inc/MantidAlgorithms/TimeAtSampleStrategyIndirect.h + inc/MantidAlgorithms/Tomography/FBPTomopy.h + inc/MantidAlgorithms/Tomography/tomopy/fbp.h + inc/MantidAlgorithms/Tomography/tomopy/utils.h inc/MantidAlgorithms/Transpose.h inc/MantidAlgorithms/UnGroupWorkspace.h inc/MantidAlgorithms/UnaryOperation.h @@ -581,7 +593,7 @@ set(SRC_UNITY_IGNORE_FILES src/AlignDetectors.cpp if(UNITY_BUILD) include(UnityBuild) - enable_unity_build(Algorithms SRC_FILES SRC_UNITY_IGNORE_FILES 10) + enable_unity_build(Algorithms SRC_FILES C_SRC_FILES SRC_UNITY_IGNORE_FILES 10) endif(UNITY_BUILD) set ( TEST_FILES @@ -722,6 +734,7 @@ set ( TEST_FILES He3TubeEfficiencyTest.h IQTransformTest.h IdentifyNoisyDetectorsTest.h + ImggTomographicReconstructionTest.h IntegrateByComponentTest.h IntegrationTest.h InterpolatingRebinTest.h @@ -832,6 +845,7 @@ set ( TEST_FILES TimeAtSampleStrategyDirectTest.h TimeAtSampleStrategyElasticTest.h TimeAtSampleStrategyIndirectTest.h + Tomography/FBPTomopyTest.h TransposeTest.h UnGroupWorkspaceTest.h UnaryOperationTest.h @@ -848,7 +862,7 @@ set ( TEST_FILES set ( TEST_PY_FILES NormaliseToUnityTest.py ) if (COVERALLS) - foreach( loop_var ${SRC_FILES} ${INC_FILES}) + foreach( loop_var ${SRC_FILES} ${C_SRC_FILES} ${INC_FILES}) set_property(GLOBAL APPEND PROPERTY COVERAGE_SRCS "${CMAKE_CURRENT_SOURCE_DIR}/${loop_var}") endforeach(loop_var) endif() @@ -856,7 +870,7 @@ endif() # Add a precompiled header where they are supported enable_precompiled_headers ( inc/MantidAlgorithms/PrecompiledHeader.h SRC_FILES ) # Add the target for this directory -add_library ( Algorithms ${SRC_FILES} ${INC_FILES}) +add_library ( Algorithms ${SRC_FILES} ${C_SRC_FILES} ${INC_FILES}) # Set the name of the generated library set_target_properties ( Algorithms PROPERTIES OUTPUT_NAME MantidAlgorithms COMPILE_DEFINITIONS "IN_MANTID_ALGORITHMS" ) diff --git a/Framework/Algorithms/inc/MantidAlgorithms/ImggTomographicReconstruction.h b/Framework/Algorithms/inc/MantidAlgorithms/ImggTomographicReconstruction.h new file mode 100644 index 0000000000000000000000000000000000000000..6edb67009036ed4738f71087988a8b14ea9cbe79 --- /dev/null +++ b/Framework/Algorithms/inc/MantidAlgorithms/ImggTomographicReconstruction.h @@ -0,0 +1,81 @@ +#ifndef MANTID_ALGORITHMS_IMGGTOMOGRAPHICRECONSTRUCTION_H_ +#define MANTID_ALGORITHMS_IMGGTOMOGRAPHICRECONSTRUCTION_H_ + +#include "MantidAlgorithms/DllConfig.h" +#include "MantidAPI/Algorithm.h" +#include "MantidAPI/WorkspaceGroup_fwd.h" + +namespace Mantid { +namespace Algorithms { + +/** + ImggTomographicReconstruction: reconstruct volumes from a sequence + of 2D projections using tomographic reconstruction methods. + + Copyright © 2016 ISIS Rutherford Appleton Laboratory, NScD Oak Ridge + National Laboratory & European Spallation Source + + This file is part of Mantid. + + Mantid 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. + + Mantid 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 this program. If not, see <http://www.gnu.org/licenses/>. + + File change history is stored at: <https://github.com/mantidproject/mantid> + Code Documentation is available at: <http://doxygen.mantidproject.org> +*/ +class MANTID_ALGORITHMS_DLL ImggTomographicReconstruction final + : public API::Algorithm { +public: + const std::string name() const override final; + + int version() const override final; + + const std::string category() const override final; + + const std::string summary() const override final; + +private: + void init() override final; + + void exec() override final; + + bool processGroups() override final; + + std::map<std::string, std::string> validateInputs() override; + + std::unique_ptr<std::vector<float>> + prepareProjectionAngles(API::WorkspaceGroup_const_sptr wks, double minAngle, + double maxAngle) const; + + std::unique_ptr<std::vector<float>> + prepareInputData(size_t totalSize, API::WorkspaceGroup_const_sptr wsg); + + std::unique_ptr<std::vector<float>> prepareDataVol(size_t totalSize); + + std::unique_ptr<std::vector<float>> prepareCenters(int cor, size_t totalSize); + + size_t xSizeProjections(API::WorkspaceGroup_const_sptr wks) const; + + size_t pSizeProjections(API::WorkspaceGroup_const_sptr wks) const; + + size_t ySizeProjections(API::WorkspaceGroup_const_sptr wks) const; + + API::WorkspaceGroup_sptr buildOutputWks(const std::vector<float> &dataVol, + size_t xsize, size_t ysize, + size_t sliceSize); +}; + +} // namespace Algorithms +} // namespace Mantid + +#endif /* MANTID_ALGORITHMS_IMGGTOMOGRAPHICRECONSTRUCTION_H_ */ diff --git a/Framework/Algorithms/inc/MantidAlgorithms/Tomography/FBPTomopy.h b/Framework/Algorithms/inc/MantidAlgorithms/Tomography/FBPTomopy.h new file mode 100644 index 0000000000000000000000000000000000000000..0d295f82a82b2a2e9195e343c9b8031950720be0 --- /dev/null +++ b/Framework/Algorithms/inc/MantidAlgorithms/Tomography/FBPTomopy.h @@ -0,0 +1,45 @@ +#ifndef MANTID_ALGORITHMS_TOMOGRAPHY_FBPTOMOPY_H_ +#define MANTID_ALGORITHMS_TOMOGRAPHY_FBPTOMOPY_H_ + +#include "MantidAlgorithms/DllConfig.h" + +namespace Mantid { +namespace Algorithms { +namespace Tomography { + +/** + Wrapper for C function(s) that implement tomopy reconstruction + methods. + + Copyright © 2016 ISIS Rutherford Appleton Laboratory, NScD Oak Ridge + National Laboratory & European Spallation Source + + This file is part of Mantid. + + Mantid 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. + + Mantid 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 this program. If not, see <http://www.gnu.org/licenses/>. + + File change history is stored at: <https://github.com/mantidproject/mantid> + Code Documentation is available at: <http://doxygen.mantidproject.org> +*/ + +/// FBP - Filtered Back-Projection routine as implemented in tomopy +void MANTID_ALGORITHMS_DLL FBPTomopy(const float *data, int dy, int dt, int dx, + const float *center, const float *theta, + float *recon, int ngridx, int ngridy); + +} // namespace Tomography +} // namespace Algorithms +} // namespace Mantid + +#endif /* MANTID_ALGORITHMS_TOMOGRAPHY_FBPTOMOPY_H_ */ diff --git a/Framework/Algorithms/inc/MantidAlgorithms/Tomography/tomopy/fbp.h b/Framework/Algorithms/inc/MantidAlgorithms/Tomography/tomopy/fbp.h new file mode 100644 index 0000000000000000000000000000000000000000..ade127406daee9f2e30484d34ec32badcf192fbc --- /dev/null +++ b/Framework/Algorithms/inc/MantidAlgorithms/Tomography/tomopy/fbp.h @@ -0,0 +1,34 @@ +/** + fbp function from Tomopy - FBP reconstruction method + + Copyright © 2016 ISIS Rutherford Appleton Laboratory, NScD Oak Ridge + National Laboratory & European Spallation Source + + This file is part of Mantid. + + Mantid 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. + + Mantid 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 this program. If not, see <http://www.gnu.org/licenses/>. + + File change history is stored at: <https://github.com/mantidproject/mantid> + Code Documentation is available at: <http://doxygen.mantidproject.org> +*/ +#ifndef MANTID_ALGORITHMS_TOMOGRAPHY_TOMOPY_FBP_H_ +#define MANTID_ALGORITHMS_TOMOGRAPHY_TOMOPY_FBP_H_ + +extern "C" { +void fbp(const float *data, int dy, int dt, int dx, const float *center, + const float *theta, float *recon, int ngridx, int ngridy, + const char * /*fname*/, const float * /*filter_par*/); +} + +#endif /* MANTID_ALGORITHMS_TOMOGRAPHY_TOMOPY_FBP_H_ */ diff --git a/Framework/Algorithms/inc/MantidAlgorithms/Tomography/tomopy/utils.h b/Framework/Algorithms/inc/MantidAlgorithms/Tomography/tomopy/utils.h new file mode 100644 index 0000000000000000000000000000000000000000..607160a3be2b50c29d7f6644b2c601fa160cd1ee --- /dev/null +++ b/Framework/Algorithms/inc/MantidAlgorithms/Tomography/tomopy/utils.h @@ -0,0 +1,103 @@ +/** + Common functions for different reconstruction methods in Tomopy + + Copyright © 2016 ISIS Rutherford Appleton Laboratory, NScD Oak Ridge + National Laboratory & European Spallation Source + + This file is part of Mantid. + + Mantid 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. + + Mantid 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 this program. If not, see <http:www.gnu.org/licenses/>. + + File change history is stored at: <https:github.com/mantidproject/mantid> + Code Documentation is available at: <http:doxygen.mantidproject.org> +*/ +/** + * This file is substantially modified but originally based on the + * file utils.h from tomopy (available from + * https:github.com/tomopy/tomopy/blob/master/src/utils.h) which is: + */ +/* + Copyright (c) 2015, UChicago Argonne, LLC. All rights reserved. + + Copyright 2015. UChicago Argonne, LLC. This software was produced + under U.S. Government contract DE-AC02-06CH11357 for Argonne National + Laboratory (ANL), which is operated by UChicago Argonne, LLC for the + U.S. Department of Energy. The U.S. Government has rights to use, + reproduce, and distribute this software. NEITHER THE GOVERNMENT NOR + UChicago Argonne, LLC MAKES ANY WARRANTY, EXPRESS OR IMPLIED, OR + ASSUMES ANY LIABILITY FOR THE USE OF THIS SOFTWARE. If software is + modified to produce derivative works, such modified software should + be clearly marked, so as not to confuse it with the version available + from ANL. + + Additionally, redistribution and use in source and binary forms, with + or without modification, are permitted provided that the following + conditions are met: + + * Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + + * Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in + the documentation and/or other materials provided with the + distribution. + + * Neither the name of UChicago Argonne, LLC, Argonne National + Laboratory, ANL, the U.S. Government, nor the names of its + contributors may be used to endorse or promote products derived + from this software without specific prior written permission. + + THIS SOFTWARE IS PROVIDED BY UChicago Argonne, LLC AND CONTRIBUTORS + "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS + FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL UChicago + Argonne, LLC OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, + INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, + BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER + CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN + ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + POSSIBILITY OF SUCH DAMAGE. +*/ +#ifndef MANTID_ALGORITHMS_TOMOGRAPHY_TOMOPY_UTILS_H_ +#define MANTID_ALGORITHMS_TOMOGRAPHY_TOMOPY_UTILS_H_ + +void preprocessing(int ngridx, int ngridy, int dz, float center, float *mov, + float *gridx, float *gridy); + +int calc_quadrant(float theta_p); + +void calc_coords(int ngridx, int ngridy, float xi, float yi, float sin_p, + float cos_p, const float *gridx, const float *gridy, + float *coordx, float *coordy); + +void trim_coords(int ngridx, int ngridy, const float *coordx, + const float *coordy, const float *gridx, const float *gridy, + int *asize, float *ax, float *ay, int *bsize, float *bx, + float *by); + +void sort_intersections(int ind_condition, int asize, const float *ax, + const float *ay, int bsize, const float *bx, + const float *by, int *csize, float *coorx, + float *coory); + +void calc_dist(int ngridx, int ngridy, int csize, const float *coorx, + const float *coory, int *indi, float *dist); + +void calc_simdata(int s, int p, int d, int ngridx, int ngridy, int dt, int dx, + int csize, const int *indi, const float *dist, + const float *model, float *simdata); + +#endif /* MANTID_ALGORITHMS_TOMOGRAPHY_TOMOPY_UTILS_H_ */ diff --git a/Framework/Algorithms/src/ImggTomographicReconstruction.cpp b/Framework/Algorithms/src/ImggTomographicReconstruction.cpp new file mode 100644 index 0000000000000000000000000000000000000000..e6c8ac35556d2f529e5455c5d297677b7d4f6cb7 --- /dev/null +++ b/Framework/Algorithms/src/ImggTomographicReconstruction.cpp @@ -0,0 +1,412 @@ +#include "MantidAlgorithms/ImggTomographicReconstruction.h" +#include "MantidAlgorithms/Tomography/FBPTomopy.h" +#include "MantidAPI/AlgorithmManager.h" +#include "MantidAPI/CommonBinsValidator.h" +#include "MantidAPI/FileProperty.h" +#include "MantidAPI/HistogramValidator.h" +#include "MantidAPI/MatrixWorkspace.h" +#include "MantidAPI/WorkspaceUnitValidator.h" +#include "MantidAPI/WorkspaceFactory.h" +#include "MantidAPI/WorkspaceGroup.h" +#include "MantidDataObjects/Workspace2D.h" +#include "MantidKernel/BoundedValidator.h" +#include "MantidKernel/CompositeValidator.h" +#include "MantidKernel/ListValidator.h" +#include "MantidKernel/make_unique.h" + +namespace Mantid { +namespace Algorithms { + +// Register the algorithm into the AlgorithmFactory +DECLARE_ALGORITHM(ImggTomographicReconstruction) + +namespace { +// Just to have explicit double => float casts in std::copy/transform +struct DoubleToFloatStd { + float operator()(const double &dblValue) const { + return static_cast<float>(dblValue); + } +}; +} + +//---------------------------------------------------------------------------------------------- + +/// Algorithms name for identification. @see Algorithm::name +const std::string ImggTomographicReconstruction::name() const { + return "ImggTomographicReconstruction"; +} + +/// Algorithm's version for identification. @see Algorithm::version +int ImggTomographicReconstruction::version() const { return 1; } + +/// Algorithm's category for identification. @see Algorithm::category +const std::string ImggTomographicReconstruction::category() const { + return "Diffraction\\Imaging;Diffraction\\Tomography"; +} + +/// Algorithm's summary for use in the GUI and help. @see Algorithm::summary +const std::string ImggTomographicReconstruction::summary() const { + return "Reconstruct a 3D volume from 2D imaging projection data"; +} + +namespace { +const std::string PROP_INPUT_WS = "InputWorkspace"; +const std::string PROP_METHOD = "Method"; +const std::string PROP_OUTPUT_WS = "OutputWorkspace"; +const std::string PROP_COR = "CenterOfRotation"; +const std::string PROP_RELAXATION_PARAM = "RelaxationParameter"; +const std::string PROP_MAX_CORES = "MaximumCores"; +const std::string PROP_MIN_PROJ_ANGLE = "MinProjectionAngle"; +const std::string PROP_MAX_PROJ_ANGLE = "MaxProjectionAngle"; +} + +//---------------------------------------------------------------------------------------------- +/** Initialize the algorithm's properties. + */ +void ImggTomographicReconstruction::init() { + + declareProperty( + Kernel::make_unique<API::WorkspaceProperty<API::Workspace>>( + PROP_INPUT_WS, "", Kernel::Direction::Input), + "Group of workspace holding images (with one spectrum per pixel row)."); + + std::vector<std::string> methods{"FBP (tomopy)"}; + declareProperty( + PROP_METHOD, methods.front(), + boost::make_shared<Kernel::ListValidator<std::string>>(methods), + "Reconstruction method", Kernel::Direction::Input); + + declareProperty( + Kernel::make_unique<API::WorkspaceProperty<API::WorkspaceGroup>>( + PROP_OUTPUT_WS, "", Kernel::Direction::Output), + "Output reconstructed volume, as a group of workspaces where " + "each workspace holds one slice of the volume."); + + auto zeroOrPosInt = boost::make_shared<Kernel::BoundedValidator<int>>(); + zeroOrPosInt->setLower(-1); + declareProperty(PROP_COR, -1, zeroOrPosInt, + "Center of rotation for the reconstruction (in pixels)."); + + auto zeroOrPosDbl = boost::make_shared<Kernel::BoundedValidator<double>>(); + zeroOrPosDbl->setLower(0.0); + declareProperty(PROP_RELAXATION_PARAM, 0.5, zeroOrPosDbl, + "Relaxation parameter for the reconstruction method."); + + zeroOrPosInt->setLower(0); + declareProperty(PROP_MAX_CORES, 0, zeroOrPosInt, + "Maximum number of cores to use for parallel runs. Leave " + "empty to use all available cores."); + + declareProperty(PROP_MIN_PROJ_ANGLE, 0.0, zeroOrPosDbl, + "Minimum projection angle."); + + declareProperty(PROP_MAX_PROJ_ANGLE, 180.0, zeroOrPosDbl, + "Maximum projection angle (assuming a uniform angle increase " + "from first to last projection."); +} + +std::map<std::string, std::string> +ImggTomographicReconstruction::validateInputs() { + std::map<std::string, std::string> result; + + API::Workspace_const_sptr inWks = getProperty(PROP_INPUT_WS); + API::WorkspaceGroup_const_sptr inGrp = + boost::dynamic_pointer_cast<const API::WorkspaceGroup>(inWks); + if (!inGrp) { + result[PROP_INPUT_WS] = "The current version of this algorithm only " + "supports input workspaces of type WorkspaceGroup"; + } else { + if (inGrp->size() < 2) { + result[PROP_INPUT_WS] = "The input workspace must have at least two " + "workspaces (projection images)"; + } + + auto first = inGrp->getItem(0); + auto fwks = boost::dynamic_pointer_cast<API::MatrixWorkspace>(first); + if (!fwks) { + result[PROP_INPUT_WS] = + "Unable to get a matrix workspace from the first " + "item of the input workspace group " + + first->getTitle() + + ". It must contain workspaces of type MatrixWorkspace"; + } else { + int cor = getProperty(PROP_COR); + size_t bsize = fwks->blocksize(); + if (cor < 0 || cor >= static_cast<int>(bsize)) { + result[PROP_COR] = + "The center of rotation must be between 0 and the " + "number of columns in the input projection images (0 to " + + std::to_string(bsize - 1) + ")"; + } + } + // Not validating requirements on all input workspaces here (there could be + // many) + } + + double minAngle = getProperty(PROP_MIN_PROJ_ANGLE); + double maxAngle = getProperty(PROP_MAX_PROJ_ANGLE); + + if (minAngle >= maxAngle) { + result[PROP_MIN_PROJ_ANGLE] = PROP_MIN_PROJ_ANGLE + + " cannot be equal to or lower than " + + PROP_MAX_PROJ_ANGLE; + } + return result; +} + +//---------------------------------------------------------------------------------------------- +/** Execute the algorithm. + */ +void ImggTomographicReconstruction::exec() { + throw std::runtime_error( + "This algorithm cannot be executed with a single workspace as input"); +} + +bool ImggTomographicReconstruction::processGroups() { + API::Workspace_const_sptr inWks = getProperty("InputWorkspace"); + + API::WorkspaceGroup_const_sptr wks = + boost::dynamic_pointer_cast<const API::WorkspaceGroup>(inWks); + + if (!wks) { + g_log.error( + "Could not retrieve the input workspace as a workspace group: "); + return false; + } + + // TODO: apply validators here on every input image/workspace + for (size_t idx = 0; idx < wks->size(); idx++) { + auto item = wks->getItem(0); + auto mWS = boost::dynamic_pointer_cast<API::MatrixWorkspace>(item); + if (!mWS) { + throw std::runtime_error("Unable to get a matrix workspace from the " + "element of te workspace group with title " + + item->getTitle()); + } + + auto wsValidator = boost::make_shared<Kernel::CompositeValidator>(); + wsValidator->add<API::CommonBinsValidator>(); + wsValidator->add<API::HistogramValidator>(); + // Probably we won't need this: + // wsValidator->add<API::WorkspaceUnitValidator>("Label"); + const std::string validation = wsValidator->isValid(mWS); + if (validation != "") { + throw std::runtime_error( + "Validation of input image / matrix workspace failed: " + validation); + } + } + + double minAngle = getProperty(PROP_MIN_PROJ_ANGLE); + double maxAngle = getProperty(PROP_MAX_PROJ_ANGLE); + auto angles = prepareProjectionAngles(wks, minAngle, maxAngle); + + // these values are expected as 'int' by the tompy routines + const int ysize = static_cast<int>(ySizeProjections(wks)); + const int projSize = static_cast<int>(angles->size()); + const int xsize = static_cast<int>(xSizeProjections(wks)); + // total size of input data in voxels + size_t totalInSize = ysize * projSize * xsize; + // total size of the reconstructed volume + size_t totalReconSize = ysize * ysize * xsize; + + auto inVol = prepareInputData(totalInSize, wks); + auto reconVol = prepareDataVol(totalReconSize); + + int cor = getProperty(PROP_COR); + auto centers = prepareCenters(cor, ysize); + + Mantid::Algorithms::Tomography::FBPTomopy( + inVol->data(), ysize, projSize, xsize, centers->data(), angles->data(), + reconVol->data(), xsize, ysize); + + size_t expectedVox = ysize * ysize * xsize; + if (reconVol->size() != expectedVox) { + std::stringstream stream; + stream << std::string("The reconstructed volume data block does not " + "have the expected dimensions. It has ") + << reconVol->size() << " voxels, whereas I was expecting: " << ysize + << " slices by " << ysize << " rows by " << xsize + << " columns = " << expectedVox << " voxels in total"; + throw std::runtime_error(stream.str()); + } + + const auto outputGrp = buildOutputWks(*reconVol, xsize, ysize, ysize); + setProperty(PROP_OUTPUT_WS, outputGrp); + + g_log.notice() << "Finished reconstruction of volume from workspace " + << wks->getTitle() << " with " << projSize + << " input projections, " << ysize << " rows by " << xsize + << " columns." << std::endl; + + // This is an ugly workaround for the issue that processGroups() + // does not fully finish the algorithm: + // https://github.com/mantidproject/mantid/issues/11361 + // A similar workaround is used in CompareWorkspaces and others + // Store output workspace in AnalysisDataService and finish execution + if (!isChild()) + this->store(); + setExecuted(true); + notificationCenter().postNotification( + new FinishedNotification(this, this->isExecuted())); + + return true; +} + +std::unique_ptr<std::vector<float>> +ImggTomographicReconstruction::prepareProjectionAngles( + API::WorkspaceGroup_const_sptr wks, double minAngle, + double maxAngle) const { + + auto angles = Kernel::make_unique<std::vector<float>>(wks->size()); + + auto vec = *angles; + double factor = (maxAngle - minAngle); + for (size_t idx = 0; idx < wks->size(); ++idx) { + vec[idx] = static_cast<float>(minAngle + + factor * static_cast<double>(idx) / + static_cast<double>(idx - 1)); + } + + return angles; +} + +std::unique_ptr<std::vector<float>> +ImggTomographicReconstruction::prepareInputData( + size_t totalSize, API::WorkspaceGroup_const_sptr wksg) { + auto data = prepareDataVol(totalSize); + + if (!wksg || 0 == wksg->size()) + return data; + + auto first = wksg->getItem(0); + auto fwks = boost::dynamic_pointer_cast<API::MatrixWorkspace>(first); + if (!fwks) { + throw std::runtime_error( + "Unable to get a matrix workspace from the first " + "item of the input workspace group " + + first->getTitle() + + ". It must contain workspaces of type MatrixWorkspace"); + } + + size_t ysize = fwks->getNumberHistograms(); + size_t xsize = fwks->blocksize(); + const size_t oneSliceSize = xsize * ysize; + + PARALLEL_FOR_NO_WSP_CHECK() + for (int slice = 0; slice < static_cast<int>(wksg->size()); ++slice) { + size_t startSlice = slice * oneSliceSize; + for (size_t row = 0; row < ysize; ++row) { + const Mantid::API::ISpectrum *specRow = fwks->getSpectrum(row); + const auto &dataY = specRow->readY(); + size_t startRow = startSlice + row * ysize; + // MSVC will produce C4244 warnings in <xutility> (double=>float + // converstion) + // std::copy(dataY.begin(), dataY.end(), data->begin() + startRow); + std::transform(dataY.begin(), dataY.end(), data->begin() + startRow, + DoubleToFloatStd()); + } + } + + return data; +} + +std::unique_ptr<std::vector<float>> +ImggTomographicReconstruction::prepareDataVol(size_t totalSize) { + return Kernel::make_unique<std::vector<float>>(totalSize); +} + +std::unique_ptr<std::vector<float>> +ImggTomographicReconstruction::prepareCenters(int cor, size_t totalSize) { + auto centers = Kernel::make_unique<std::vector<float>>(totalSize); + + for (auto &cnt : *centers) { + cnt = static_cast<float>(cor); + } + + return centers; +} + +size_t ImggTomographicReconstruction::xSizeProjections( + API::WorkspaceGroup_const_sptr wks) const { + auto first = wks->getItem(0); + + auto wksItem = boost::dynamic_pointer_cast<API::MatrixWorkspace>(first); + if (!wksItem) { + throw std::runtime_error("Unable to get a matrix workspace from the first " + "item of the workspace group " + + first->getTitle()); + } + + return wksItem->getNumberHistograms(); +} + +size_t ImggTomographicReconstruction::pSizeProjections( + API::WorkspaceGroup_const_sptr wks) const { + return wks->size(); +} + +size_t ImggTomographicReconstruction::ySizeProjections( + API::WorkspaceGroup_const_sptr wks) const { + auto first = wks->getItem(0); + + auto wksItem = boost::dynamic_pointer_cast<API::MatrixWorkspace>(first); + if (!wksItem) { + throw std::runtime_error("Unable to get a matrix workspace from the first " + "item of the workspace group " + + first->getTitle()); + } + + return wksItem->blocksize(); +} + +/** + * Transfer data from a numpy/tomopy style data volume to a workspace + * group of MatrixWorkspaces. This will typically have ysize slices + * (images) of dimensions ysize rows by xsize columns. + + * + * @param dataVol a 3D data volume that may have been generated as + * output from a reconstruction + * @param xsize image x dimension or number of columns (bins) + * @param ysize image y dimension or number of rows (spectra) + * @param sliceSize number of slices + * + * @return a workspace group with reconstruction slices + */ +API::WorkspaceGroup_sptr +ImggTomographicReconstruction::buildOutputWks(const std::vector<float> &dataVol, + size_t xsize, size_t ysize, + size_t sliceSize) { + + // auto wsGroup = boost::make_shared<API::WorkspaceGroup>(); + auto wsGroup = API::WorkspaceGroup_sptr(new API::WorkspaceGroup()); + wsGroup->setTitle("Reconstructed volume from imaging projection data"); + + const size_t oneSliceSize = xsize * ysize; + PARALLEL_FOR_NO_WSP_CHECK() + for (int slice = 0; slice < static_cast<int>(sliceSize); ++slice) { + // individual slices as Workspace2D/MatrixWorkspace + DataObjects::Workspace2D_sptr sliceWS = + boost::dynamic_pointer_cast<DataObjects::Workspace2D>( + API::WorkspaceFactory::Instance().create("Workspace2D", ysize, + xsize + 1, xsize)); + size_t startSlice = slice * oneSliceSize; + for (size_t row = 0; row < ysize; ++row) { + Mantid::API::ISpectrum *specRow = sliceWS->getSpectrum(row); + auto &dataX = specRow->dataX(); + std::fill(dataX.begin(), dataX.end(), static_cast<double>(row)); + + size_t startRow = startSlice + row * ysize; + size_t endRow = startRow + xsize; + auto &dataY = specRow->dataY(); + std::transform(dataVol.begin() + startRow, dataVol.begin() + endRow, + dataY.begin(), DoubleToFloatStd()); + } + wsGroup->addWorkspace(sliceWS); + } + + return wsGroup; +} + +} // namespace Algorithms +} // namespace Mantid diff --git a/Framework/Algorithms/src/Tomography/FBPTomopy.cpp b/Framework/Algorithms/src/Tomography/FBPTomopy.cpp new file mode 100644 index 0000000000000000000000000000000000000000..807ece8f2922621c7f88bcf1332627d10141e76c --- /dev/null +++ b/Framework/Algorithms/src/Tomography/FBPTomopy.cpp @@ -0,0 +1,15 @@ +#include "MantidAlgorithms/Tomography/FBPTomopy.h" +#include "MantidAlgorithms/Tomography/tomopy/fbp.h" + +namespace Mantid { +namespace Algorithms { +namespace Tomography { + +void FBPTomopy(const float *data, int dy, int dt, int dx, const float *center, + const float *theta, float *recon, int ngridx, int ngridy) { + fbp(data, dy, dt, dx, center, theta, recon, ngridx, ngridy, nullptr, nullptr); +} + +} // namespace Tomography +} // namespace Algorithms +} // namespace Mantid diff --git a/Framework/Algorithms/src/Tomography/tomopy/fbp.c b/Framework/Algorithms/src/Tomography/tomopy/fbp.c new file mode 100644 index 0000000000000000000000000000000000000000..b0451a11807ab74b2091d415604b5a2c13189552 --- /dev/null +++ b/Framework/Algorithms/src/Tomography/tomopy/fbp.c @@ -0,0 +1,160 @@ +/** + This file is originally based on the implementation of the FBP (Filtered + Back-Projection) method of tomopy, available from + https:github.com/tomopy/tomopy/blob/master/src/fbp.c, which is: + */ +/* + Copyright (c) 2015, UChicago Argonne, LLC. All rights reserved. + + Copyright 2015. UChicago Argonne, LLC. This software was produced + under U.S. Government contract DE-AC02-06CH11357 for Argonne National + Laboratory (ANL), which is operated by UChicago Argonne, LLC for the + U.S. Department of Energy. The U.S. Government has rights to use, + reproduce, and distribute this software. NEITHER THE GOVERNMENT NOR + UChicago Argonne, LLC MAKES ANY WARRANTY, EXPRESS OR IMPLIED, OR + ASSUMES ANY LIABILITY FOR THE USE OF THIS SOFTWARE. If software is + modified to produce derivative works, such modified software should + be clearly marked, so as not to confuse it with the version available + from ANL. + + Additionally, redistribution and use in source and binary forms, with + or without modification, are permitted provided that the following + conditions are met: + + * Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + + * Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in + the documentation and/or other materials provided with the + distribution. + + * Neither the name of UChicago Argonne, LLC, Argonne National + Laboratory, ANL, the U.S. Government, nor the names of its + contributors may be used to endorse or promote products derived + from this software without specific prior written permission. + + THIS SOFTWARE IS PROVIDED BY UChicago Argonne, LLC AND CONTRIBUTORS + "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS + FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL UChicago + Argonne, LLC OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, + INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, + BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER + CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN + ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + POSSIBILITY OF SUCH DAMAGE. +*/ + +#include "MantidAlgorithms/Tomography/tomopy/utils.h" + +#include <assert.h> +#include <math.h> +#include <stdlib.h> + +void fbp(const float *data, int dy, int dt, int dx, const float *center, + const float *theta, float *recon, int ngridx, int ngridy, + const char *fname, const float *filter_par) { + + int s, p, d, n; + int quadrant; + float theta_p, sin_p, cos_p; + float mov, xi, yi; + int asize, bsize, csize; + int ind_data, ind_recon; + + float *gridx, *gridy, *coordx, *coordy, *ax, *ay, *bx, *by, *coorx, *coory, + *dist; + int *indi; + + gridx = (float *)malloc((size_t)(ngridx + 1) * sizeof(float)); + gridy = (float *)malloc((size_t)(ngridy + 1) * sizeof(float)); + coordx = (float *)malloc((size_t)(ngridy + 1) * sizeof(float)); + coordy = (float *)malloc((size_t)(ngridx + 1) * sizeof(float)); + ax = (float *)malloc((size_t)(ngridx + ngridy) * sizeof(float)); + ay = (float *)malloc((size_t)(ngridx + ngridy) * sizeof(float)); + bx = (float *)malloc((size_t)(ngridx + ngridy) * sizeof(float)); + by = (float *)malloc((size_t)(ngridx + ngridy) * sizeof(float)); + coorx = (float *)malloc((size_t)(ngridx + ngridy) * sizeof(float)); + coory = (float *)malloc((size_t)(ngridx + ngridy) * sizeof(float)); + dist = (float *)malloc((size_t)(ngridx + ngridy) * sizeof(float)); + indi = (int *)malloc((size_t)(ngridx + ngridy) * sizeof(int)); + + /* unused arguments: */ + (void)fname; + (void)filter_par; + + assert(coordx != NULL && coordy != NULL && ax != NULL && ay != NULL && + by != NULL && bx != NULL && coorx != NULL && coory != NULL && + dist != NULL && indi != NULL); + + /* For each slice */ + for (s = 0; s < dy; s++) { + preprocessing(ngridx, ngridy, dx, center[s], &mov, gridx, + gridy); /* Outputs: mov, gridx, gridy */ + + /* For each projection angle */ + for (p = 0; p < dt; p++) { + /* + Calculate the sin and cos values + of the projection angle and find + at which quadrant on the cartesian grid. + */ + theta_p = (float)fmod(theta[p], 2 * M_PI); + quadrant = calc_quadrant(theta_p); + sin_p = sinf(theta_p); + cos_p = cosf(theta_p); + + /* For each detector pixel */ + for (d = 0; d < dx; d++) { + /* Calculate coordinates */ + xi = (float)(-ngridx - ngridy); + yi = (float)(1 - dx) / 2.0f + (float)d + mov; + calc_coords(ngridx, ngridy, xi, yi, sin_p, cos_p, gridx, gridy, coordx, + coordy); + + /* Merge the (coordx, gridy) and (gridx, coordy) */ + trim_coords(ngridx, ngridy, coordx, coordy, gridx, gridy, &asize, ax, + ay, &bsize, bx, by); + + /* + Sort the array of intersection points (ax, ay) and + (bx, by). The new sorted intersection points are + stored in (coorx, coory). Total number of points + are csize. + */ + sort_intersections(quadrant, asize, ax, ay, bsize, bx, by, &csize, + coorx, coory); + + /* + Calculate the distances (dist) between the + intersection points (coorx, coory). Find the + indices of the pixels on the reconstruction grid. + */ + calc_dist(ngridx, ngridy, csize, coorx, coory, indi, dist); + + /* Update */ + ind_recon = s * ngridx * ngridy; + ind_data = d + p * dx + s * dt * dx; + for (n = 0; n < csize - 1; n++) { + recon[indi[n] + ind_recon] += data[ind_data] * dist[n]; + } + } + } + } + + free(gridx); + free(gridy); + free(coordx); + free(coordy); + free(ax); + free(ay); + free(bx); + free(by); + free(coorx); + free(coory); + free(dist); + free(indi); +} diff --git a/Framework/Algorithms/src/Tomography/tomopy/utils.c b/Framework/Algorithms/src/Tomography/tomopy/utils.c new file mode 100644 index 0000000000000000000000000000000000000000..6b5248d5bac5f037c5203170a183ab5218b05ed0 --- /dev/null +++ b/Framework/Algorithms/src/Tomography/tomopy/utils.c @@ -0,0 +1,207 @@ +/** + This file is originally based on the implementation of the FBP (Filtered + Back-Projection) method of tomopy, available from + https:github.com/tomopy/tomopy/blob/master/src/fbp.c, which is: + */ +/* + Copyright (c) 2015, UChicago Argonne, LLC. All rights reserved. + + Copyright 2015. UChicago Argonne, LLC. This software was produced + under U.S. Government contract DE-AC02-06CH11357 for Argonne National + Laboratory (ANL), which is operated by UChicago Argonne, LLC for the + U.S. Department of Energy. The U.S. Government has rights to use, + reproduce, and distribute this software. NEITHER THE GOVERNMENT NOR + UChicago Argonne, LLC MAKES ANY WARRANTY, EXPRESS OR IMPLIED, OR + ASSUMES ANY LIABILITY FOR THE USE OF THIS SOFTWARE. If software is + modified to produce derivative works, such modified software should + be clearly marked, so as not to confuse it with the version available + from ANL. + + Additionally, redistribution and use in source and binary forms, with + or without modification, are permitted provided that the following + conditions are met: + + * Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + + * Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in + the documentation and/or other materials provided with the + distribution. + + * Neither the name of UChicago Argonne, LLC, Argonne National + Laboratory, ANL, the U.S. Government, nor the names of its + contributors may be used to endorse or promote products derived + from this software without specific prior written permission. + + THIS SOFTWARE IS PROVIDED BY UChicago Argonne, LLC AND CONTRIBUTORS + "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS + FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL UChicago + Argonne, LLC OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, + INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, + BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER + CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN + ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + POSSIBILITY OF SUCH DAMAGE. +*/ + +#include "MantidAlgorithms/Tomography/tomopy/utils.h" + +#include <math.h> + +void preprocessing(int ry, int rz, int num_pixels, float center, float *mov, + float *gridx, float *gridy) { + int i; + + for (i = 0; i <= ry; i++) { + gridx[i] = ((float)-ry / 2.f + (float)i); + } + + for (i = 0; i <= rz; i++) { + gridy[i] = ((float)-rz / 2.f + (float)i); + } + + *mov = ((float)num_pixels - 1) / 2.0f - center; + if (*mov - floor(*mov) < 0.01f) { + *mov += 0.01f; + } + *mov += 0.5f; +} + +int calc_quadrant(float theta_p) { + int quadrant; + if ((theta_p >= 0 && theta_p < M_PI / 2) || + (theta_p >= M_PI && theta_p < 3 * M_PI / 2) || + (theta_p >= -M_PI && theta_p < -M_PI / 2) || + (theta_p >= -2 * M_PI && theta_p < -3 * M_PI / 2)) { + quadrant = 1; + } else { + quadrant = 0; + } + return quadrant; +} + +void calc_coords(int ry, int rz, float xi, float yi, float sin_p, float cos_p, + const float *gridx, const float *gridy, float *coordx, + float *coordy) { + float srcx, srcy, detx, dety; + float slope, islope; + int n; + + srcx = xi * cos_p - yi * sin_p; + srcy = xi * sin_p + yi * cos_p; + detx = -xi * cos_p - yi * sin_p; + dety = -xi * sin_p + yi * cos_p; + + slope = (srcy - dety) / (srcx - detx); + islope = 1 / slope; + for (n = 0; n <= ry; n++) { + coordy[n] = slope * (gridx[n] - srcx) + srcy; + } + for (n = 0; n <= rz; n++) { + coordx[n] = islope * (gridy[n] - srcy) + srcx; + } +} + +void trim_coords(int ry, int rz, const float *coordx, const float *coordy, + const float *gridx, const float *gridy, int *asize, float *ax, + float *ay, int *bsize, float *bx, float *by) { + int n; + + *asize = 0; + *bsize = 0; + for (n = 0; n <= rz; n++) { + if (coordx[n] >= gridx[0] + 1e-2) { + if (coordx[n] <= gridx[ry] - 1e-2) { + ax[*asize] = coordx[n]; + ay[*asize] = gridy[n]; + (*asize)++; + } + } + } + for (n = 0; n <= ry; n++) { + if (coordy[n] >= gridy[0] + 1e-2) { + if (coordy[n] <= gridy[rz] - 1e-2) { + bx[*bsize] = gridx[n]; + by[*bsize] = coordy[n]; + (*bsize)++; + } + } + } +} + +void sort_intersections(int ind_condition, int asize, const float *ax, + const float *ay, int bsize, const float *bx, + const float *by, int *csize, float *coorx, + float *coory) { + int i = 0, j = 0, k = 0; + int a_ind; + while (i < asize && j < bsize) { + a_ind = (ind_condition) ? i : (asize - 1 - i); + if (ax[a_ind] < bx[j]) { + coorx[k] = ax[a_ind]; + coory[k] = ay[a_ind]; + i++; + k++; + } else { + coorx[k] = bx[j]; + coory[k] = by[j]; + j++; + k++; + } + } + while (i < asize) { + a_ind = (ind_condition) ? i : (asize - 1 - i); + coorx[k] = ax[a_ind]; + coory[k] = ay[a_ind]; + i++; + k++; + } + while (j < bsize) { + coorx[k] = bx[j]; + coory[k] = by[j]; + j++; + k++; + } + *csize = asize + bsize; +} + +void calc_dist(int ry, int rz, int csize, const float *coorx, + const float *coory, int *indi, float *dist) { + int n; + + for (n = 0; n < csize - 1; n++) { + int i1, i2; + float x1, x2; + float diffx, diffy, midx, midy; + int indx, indy; + + diffx = coorx[n + 1] - coorx[n]; + diffy = coory[n + 1] - coory[n]; + dist[n] = (float)sqrt(diffx * diffx + diffy * diffy); + midx = (coorx[n + 1] + coorx[n]) / 2; + midy = (coory[n + 1] + coory[n]) / 2; + x1 = midx + (float)ry / 2.f; + x2 = midy + (float)rz / 2.f; + i1 = (int)(midx + ry / 2.); + i2 = (int)(midy + rz / 2.); + indx = i1 - (i1 > x1); + indy = i2 - (i2 > x2); + indi[n] = indy + (indx * rz); + } +} + +void calc_simdata(int s, int p, int d, int ry, int rz, int dt, int dx, + int csize, const int *indi, const float *dist, + const float *model, float *simdata) { + int n; + + int index_model = s * ry * rz; + int index_data = d + p * dx + s * dt * dx; + for (n = 0; n < csize - 1; n++) { + simdata[index_data] += model[indi[n] + index_model] * dist[n]; + } +} diff --git a/Framework/Algorithms/test/ImggTomographicReconstructionTest.h b/Framework/Algorithms/test/ImggTomographicReconstructionTest.h new file mode 100644 index 0000000000000000000000000000000000000000..a6d196ddc4b9ea0f997c9b2df42ba3088a1536a6 --- /dev/null +++ b/Framework/Algorithms/test/ImggTomographicReconstructionTest.h @@ -0,0 +1,286 @@ +#ifndef MANTID_ALGORITHMS_IMGGTOMOGRAPHICRECONSTRUCTIONTEST_H_ +#define MANTID_ALGORITHMS_IMGGTOMOGRAPHICRECONSTRUCTIONTEST_H_ + +#include <cxxtest/TestSuite.h> + +#include "MantidAlgorithms/ImggTomographicReconstruction.h" +#include "MantidAPI/AlgorithmManager.h" +#include "MantidKernel/Exception.h" + +#include "MantidTestHelpers/WorkspaceCreationHelper.h" + +#include <Poco/File.h> + +using Mantid::Algorithms::ImggTomographicReconstruction; +using namespace Mantid; + +class ImggTomographicReconstructionTest : public CxxTest::TestSuite { + +public: + // This pair of boilerplate methods prevent the suite being created statically + // This means the constructor isn't called when running other tests + static ImggTomographicReconstructionTest *createSuite() { + return new ImggTomographicReconstructionTest(); + } + + static void destroySuite(ImggTomographicReconstructionTest *suite) { + delete suite; + } + + void test_init() { + ImggTomographicReconstruction alg; + TS_ASSERT_THROWS_NOTHING(alg.initialize()); + TS_ASSERT(alg.isInitialized()); + + double relax; + TS_ASSERT_THROWS_NOTHING(relax = alg.getProperty("RelaxationParameter")); + TS_ASSERT_EQUALS(relax, 0.5); + } + + void test_errors_options() { + auto alg = API::AlgorithmManager::Instance().create( + "ImggTomographicReconstruction"); + + TS_ASSERT_THROWS_NOTHING( + alg->setPropertyValue("OutputWorkspace", "_unused_for_child")); + + TS_ASSERT_THROWS( + alg->setPropertyValue("BitDepth", "this_is_wrong_you_must_fail"), + std::runtime_error); + } + + void test_exec_fails_inexistent_workspace() { + ImggTomographicReconstruction alg; + TS_ASSERT_THROWS_NOTHING(alg.initialize()); + TS_ASSERT(alg.isInitialized()); + TS_ASSERT_THROWS( + alg.setPropertyValue("InputWorkspace", "inexistent_workspace_fails"), + std::invalid_argument); + + TS_ASSERT_THROWS(alg.execute(), std::runtime_error); + TS_ASSERT(!alg.isExecuted()); + } + + void test_exec_fails_wrong_workspace() { + API::MatrixWorkspace_sptr a = + WorkspaceCreationHelper::CreateWorkspaceSingleValue(3); + + ImggTomographicReconstruction alg; + TS_ASSERT_THROWS_NOTHING(alg.initialize()); + TS_ASSERT(alg.isInitialized()); + TS_ASSERT_THROWS_NOTHING(alg.setProperty("InputWorkspace", a)); + + TS_ASSERT_THROWS(alg.execute(), std::runtime_error); + TS_ASSERT(!alg.isExecuted()); + + API::MatrixWorkspace_sptr wsSingle = + WorkspaceCreationHelper::Create2DWorkspace(10, 10); + + ImggTomographicReconstruction algTwo; + TS_ASSERT_THROWS_NOTHING(algTwo.initialize()); + TS_ASSERT(algTwo.isInitialized()); + TS_ASSERT_THROWS_NOTHING(algTwo.setProperty("InputWorkspace", wsSingle)); + + TS_ASSERT_THROWS(algTwo.execute(), std::runtime_error); + TS_ASSERT(!algTwo.isExecuted()); + } + + void test_exec_fails_single_proj() { + const std::string projectionsGrpName("only_one_projection"); + API::WorkspaceGroup_sptr projectionsGrp = + WorkspaceCreationHelper::CreateWorkspaceGroup(1, 4, 4, + projectionsGrpName); + + ImggTomographicReconstruction alg; + TS_ASSERT_THROWS_NOTHING(alg.initialize()); + TS_ASSERT(alg.isInitialized()); + TS_ASSERT_THROWS_NOTHING(alg.setProperty("InputWorkspace", projectionsGrp)); + TS_ASSERT_THROWS_NOTHING(alg.setProperty("MinProjectionAngle", 0.0)); + TS_ASSERT_THROWS_NOTHING(alg.setProperty("MaxProjectionAngle", 260.0)); + TS_ASSERT_THROWS_NOTHING(alg.setProperty("RelaxationParameter", 1.25)); + TS_ASSERT_THROWS_NOTHING(alg.setProperty("CenterOfRotation", 4)); + + TS_ASSERT_THROWS(alg.execute(), std::runtime_error); + TS_ASSERT(!alg.isExecuted()); + } + + void test_exec_fails_wrong_center() { + const std::string projectionsGrpName("only_two_small_projections"); + API::WorkspaceGroup_sptr projectionsGrp = + WorkspaceCreationHelper::CreateWorkspaceGroup(2, 4, 4, + projectionsGrpName); + + ImggTomographicReconstruction alg; + TS_ASSERT_THROWS_NOTHING(alg.initialize()); + TS_ASSERT(alg.isInitialized()); + TS_ASSERT_THROWS_NOTHING(alg.setProperty("InputWorkspace", projectionsGrp)); + TS_ASSERT_THROWS_NOTHING(alg.setProperty("MinProjectionAngle", 0.0)); + TS_ASSERT_THROWS_NOTHING(alg.setProperty("MaxProjectionAngle", 260.0)); + TS_ASSERT_THROWS_NOTHING(alg.setProperty("RelaxationParameter", 1.25)); + TS_ASSERT_THROWS_NOTHING(alg.setProperty("CenterOfRotation", 10000)); + + // should throw because of the wrong center / outside of image dimensions + TS_ASSERT_THROWS(alg.execute(), std::runtime_error); + TS_ASSERT(!alg.isExecuted()); + } + + void test_exec_runs() { + const std::string projectionsGrpName("only_four_proj"); + int ysize = 16; + int xsize = 16; + int numProj = 4; + API::WorkspaceGroup_sptr projectionsGrp = + WorkspaceCreationHelper::CreateWorkspaceGroup(numProj, ysize, xsize, + projectionsGrpName); + + for (size_t proj = 0; proj < static_cast<size_t>(proj); ++proj) { + API::Workspace_sptr ws; + TS_ASSERT_THROWS_NOTHING(ws = projectionsGrp->getItem(proj)); + API::MatrixWorkspace_sptr projWS; + TS_ASSERT_THROWS_NOTHING( + projWS = boost::dynamic_pointer_cast<API::MatrixWorkspace>(ws)); + MantidVec &dataY = projWS->dataY(7); + TS_ASSERT_THROWS_NOTHING(std::fill(dataY.begin(), dataY.end(), 5000.0)); + for (size_t idx = 0; idx < static_cast<size_t>(ysize); ++idx) { + projWS->dataY(ysize)[ysize - idx - 1] = 987.6; + } + } + + ImggTomographicReconstruction alg; + // getProperty with workspaceGroup isn't working with + // Algorithm::processGroups. So we need retrieveWS<>() below: + const std::string reconName = "recon_1"; + auto recon = runWithValidCenter(alg, projectionsGrp, 7, reconName); + // Fix when this works well + TSM_ASSERT("Expected that getProperty would return nullptr with " + "WorkspaceGroup when using processGroups()", + !recon); + + TS_ASSERT_THROWS_NOTHING( + recon = API::AnalysisDataService::Instance() + .retrieveWS<API::WorkspaceGroup>(reconName)); + TSM_ASSERT("The reconstruction/result workspace is not valid", recon); + + size_t grpSize = recon->size(); + TSM_ASSERT_EQUALS( + "The number of items in the output/reconstruction workspace is wrong", + grpSize, ysize); + + const size_t pix1x = 5; + const size_t pix1y = 14; + const double referencePix1 = 8.0; + const size_t pix2x = 7; + const size_t pix2y = 8; + const double referencePix2 = 8.0; + for (size_t idx = 0; idx < grpSize; ++idx) { + auto wks = recon->getItem(idx); + TSM_ASSERT( + "The output workspace group should have valid slice workspaces", wks); + auto sliceWS = boost::dynamic_pointer_cast<API::MatrixWorkspace>(wks); + TSM_ASSERT("The slice workspaces should be of type MatrixWorkspace", + sliceWS); + + TSM_ASSERT_EQUALS("Unexpected number of columns in output slices", + sliceWS->blocksize(), xsize); + TSM_ASSERT_EQUALS("Unexpected number of rows in output slices", + sliceWS->getNumberHistograms(), ysize); + TSM_ASSERT_DELTA("Unexpected value in output pixel", + sliceWS->readY(pix1y)[pix1x], referencePix1, 1e-4); + TSM_ASSERT_DELTA("Unexpected value in output pixel", + sliceWS->readY(pix2y)[pix2x], referencePix2, 1e-4); + } + } + + void test_exec_runs0s() { + const std::string projectionsGrpName("a_couple_0_images"); + int ysize = 8; + int xsize = 8; + int numProj = 2; + API::WorkspaceGroup_sptr projectionsGrp = + WorkspaceCreationHelper::CreateWorkspaceGroup(numProj, ysize, xsize, + projectionsGrpName); + + for (size_t proj = 0; proj < static_cast<size_t>(proj); ++proj) { + API::Workspace_sptr ws; + TS_ASSERT_THROWS_NOTHING(ws = projectionsGrp->getItem(proj)); + API::MatrixWorkspace_sptr projWS; + TS_ASSERT_THROWS_NOTHING( + projWS = boost::dynamic_pointer_cast<API::MatrixWorkspace>(ws)); + for (size_t row = 0; row < static_cast<size_t>(ysize); ++row) { + MantidVec &dataY = projWS->dataY(row); + TS_ASSERT_THROWS_NOTHING(std::fill(dataY.begin(), dataY.end(), 0.0)); + } + } + + ImggTomographicReconstruction alg; + const std::string reconName = "recon_0"; + auto recon = runWithValidCenter(alg, projectionsGrp, 7, reconName); + // Fix when this works well + TSM_ASSERT("Expected that getProperty would return nullptr with " + "WorkspaceGroup when using processGroups()", + !recon); + + TS_ASSERT_THROWS_NOTHING( + recon = API::AnalysisDataService::Instance() + .retrieveWS<API::WorkspaceGroup>(reconName)); + TSM_ASSERT("The reconstruction/result workspace is not valid", recon); + + size_t grpSize = recon->size(); + TSM_ASSERT_EQUALS( + "The number of items in the output/reconstruction workspace is wrong", + grpSize, ysize); + + const size_t pix1x = 5; + const size_t pix1y = 2; + const double referencePix1 = 0.0; + const size_t pix2x = 7; + const size_t pix2y = 7; + const double referencePix2 = 0.0; + for (size_t idx = 0; idx < grpSize; ++idx) { + auto wks = recon->getItem(idx); + TSM_ASSERT( + "The output workspace group should have valid slice workspaces", wks); + auto sliceWS = boost::dynamic_pointer_cast<API::MatrixWorkspace>(wks); + TSM_ASSERT("The slice workspaces should be of type MatrixWorkspace", + sliceWS); + + TSM_ASSERT_EQUALS("Unexpected number of columns in output slices", + sliceWS->blocksize(), xsize); + TSM_ASSERT_EQUALS("Unexpected number of rows in output slices", + sliceWS->getNumberHistograms(), ysize); + TSM_ASSERT_DELTA("Unexpected value in output pixel", + sliceWS->readY(pix1y)[pix1x], referencePix1, 1e-4); + TSM_ASSERT_DELTA("Unexpected value in output pixel", + sliceWS->readY(pix2y)[pix2x], referencePix2, 1e-4); + } + } + +private: + API::WorkspaceGroup_sptr runWithValidCenter(API::Algorithm &alg, + API::WorkspaceGroup_sptr wksg, + int center, + const std::string &outName) { + TS_ASSERT_THROWS_NOTHING(alg.initialize()); + TS_ASSERT(alg.isInitialized()); + TS_ASSERT_THROWS_NOTHING(alg.setProperty("InputWorkspace", wksg)); + TS_ASSERT_THROWS_NOTHING(alg.setProperty("MinProjectionAngle", 0.0)); + TS_ASSERT_THROWS_NOTHING(alg.setProperty("MaxProjectionAngle", 180.0)); + TS_ASSERT_THROWS_NOTHING(alg.setProperty("RelaxationParameter", 1.25)); + TS_ASSERT_THROWS_NOTHING(alg.setProperty("CenterOfRotation", center)); + + TS_ASSERT_THROWS_NOTHING(alg.setPropertyValue("OutputWorkspace", outName)); + TSM_ASSERT_THROWS_NOTHING( + "execute() throwed for an algorithm with a " + "supposedly correct center parameter for which no " + "failure was expected", + alg.execute()); + TSM_ASSERT("The algorithm execution didn't finish successfully when no " + "issues where expected ", + alg.isExecuted()); + + API::WorkspaceGroup_sptr result = alg.getProperty("OutputWorkspace"); + return result; + } +}; + +#endif /* MANTID_ALGORITHMS_IMGGTOMOGRAPHICRECONSTRUCTIONTEST_H_ */ diff --git a/Framework/Algorithms/test/Tomography/FBPTomopyTest.h b/Framework/Algorithms/test/Tomography/FBPTomopyTest.h new file mode 100644 index 0000000000000000000000000000000000000000..091844e61345c8e001ad15d24440fb212fcd8982 --- /dev/null +++ b/Framework/Algorithms/test/Tomography/FBPTomopyTest.h @@ -0,0 +1,114 @@ +#ifndef MANTID_ALGORITHMS_FBPTOMOPYTEST_H_ +#define MANTID_ALGORITHMS_FBPTOMOPYTEST_H_ + +#include <cxxtest/TestSuite.h> + +#include "MantidAlgorithms/Tomography/FBPTomopy.h" +#include "MantidAPI/AlgorithmManager.h" +#include "MantidKernel/Exception.h" + +#include "MantidTestHelpers/WorkspaceCreationHelper.h" + +#include <numeric> + +using namespace Mantid::Algorithms::Tomography; + +class FBPTomopyTest : public CxxTest::TestSuite { + +public: + // This pair of boilerplate methods prevent the suite being created statically + // This means the constructor isn't called when running other tests + static FBPTomopyTest *createSuite() { return new FBPTomopyTest(); } + + static void destroySuite(FBPTomopyTest *suite) { delete suite; } + + void test_null() { + FBPTomopy(nullptr, 0, 0, 0, nullptr, nullptr, nullptr, 0, 0); + } + + void test_small_buffer_flat() { + const size_t numProj = 3; + const size_t ysize = 8; + const size_t xsize = 8; + const size_t projSize = numProj * ysize * xsize; + std::array<float, projSize> projImages; + std::fill(projImages.begin(), projImages.end(), 33.0f); + + const size_t reconSize = ysize * ysize * xsize; + std::array<float, reconSize> reconVol; + std::fill(reconVol.begin(), reconVol.end(), 0.0f); + + const size_t numAngles = numProj; + std::array<float, numAngles> angles{{0.0, 90.0, 180.0}}; + + const size_t numCenters = ysize; + std::array<float, numCenters> centers{{4, 3, 3, 4, 4, 4, 4, 3}}; + + FBPTomopy(projImages.data(), ysize, numProj, xsize, centers.data(), + angles.data(), reconVol.data(), xsize, ysize); + + TS_ASSERT_DELTA(reconVol.front(), 66.7519, 1); + TS_ASSERT_DELTA(reconVol[1], 67.9431, 1); + TS_ASSERT_DELTA(reconVol[2], 90.9802, 1); + TS_ASSERT_DELTA(reconVol[3], 111.1035, 1); + TS_ASSERT_DELTA(reconVol[4], 88.6847, 1); + TS_ASSERT_DELTA(reconVol[20], 102.3709, 1); + TS_ASSERT_DELTA(reconVol[50], 97.4173, 1); + TS_ASSERT_DELTA(reconVol[100], 97.4172, 1); + TS_ASSERT_DELTA(reconVol[150], 99.8273, 1); + TS_ASSERT_DELTA(reconVol[projSize - 5], 82.4248, 1); + TS_ASSERT_DELTA(reconVol[projSize - 4], 74.1906, 1); + TS_ASSERT_DELTA(reconVol[projSize - 3], 60.5043, 1); + TS_ASSERT_DELTA(reconVol[projSize - 2], 67.9431, 1); + TS_ASSERT_DELTA(reconVol.back(), 66.7519, 1); + } + + void test_buffer_idx() { + const size_t numProj = 8; + const size_t ysize = 16; + const size_t xsize = 16; + const size_t projSize = numProj * ysize * xsize; + std::array<float, projSize> projImages; + std::iota(projImages.begin(), projImages.end(), 0.0f); + + // inconsistent/stressing values + std::fill(projImages.begin() + 300, projImages.begin() + 400, 333.0f); + std::fill(projImages.begin() + 600, projImages.begin() + 850, 999.0f); + std::fill(projImages.begin() + 990, projImages.begin() + 1100, 1000.0f); + std::fill(projImages.begin() + 1500, projImages.begin() + 1700, -444.0f); + std::fill(projImages.begin() + 1900, projImages.begin() + 2000, 765.0f); + + const size_t reconSize = ysize * ysize * xsize; + std::array<float, reconSize> reconVol; + std::fill(reconVol.begin(), reconVol.end(), 0.0f); + + const size_t numAngles = numProj; + std::array<float, numAngles> angles{ + {0.0, 45.0, 90.0, 135.0, 180.0, 225.0, 270.0, 315.0}}; + + const size_t numCenters = ysize; + std::array<float, numCenters> centers; + std::fill(centers.begin(), centers.end(), 7.5f); + + FBPTomopy(projImages.data(), ysize, numProj, xsize, centers.data(), + angles.data(), reconVol.data(), xsize, ysize); + + TS_ASSERT_DELTA(reconVol.front(), 241.6610, 1); + TS_ASSERT_DELTA(reconVol[1], 286.6727, 1); + TS_ASSERT_DELTA(reconVol[2], 392.6853, 1); + TS_ASSERT_DELTA(reconVol[3], 350.0282, 1); + TS_ASSERT_DELTA(reconVol[4], 429.8395, 1); + TS_ASSERT_DELTA(reconVol[200], 516.5272, 1); + TS_ASSERT_DELTA(reconVol[500], 1202.6435, 1); + TS_ASSERT_DELTA(reconVol[1000], 3604.5090, 1); + TS_ASSERT_DELTA(reconVol[1500], 8092.9765, 1); + TS_ASSERT_DELTA(reconVol[1900], 7941.9135, 1); + TS_ASSERT_DELTA(reconVol[projSize - 5], 6829.1318, 1); + TS_ASSERT_DELTA(reconVol[projSize - 4], 5662.6342, 1); + TS_ASSERT_DELTA(reconVol[projSize - 3], 5747.1845, 1); + TS_ASSERT_DELTA(reconVol[projSize - 2], 6034.2163, 1); + TS_ASSERT_DELTA(reconVol.back(), 3736.5483, 1); + } +}; + +#endif /* MANTID_ALGORITHM_FBPTOMOPYTEST_H_ */ diff --git a/docs/source/algorithms/ImggTomographicReconstruction-v1.rst b/docs/source/algorithms/ImggTomographicReconstruction-v1.rst new file mode 100644 index 0000000000000000000000000000000000000000..709b0f076e925ef5a3d3f2e776e70e12860a4d8b --- /dev/null +++ b/docs/source/algorithms/ImggTomographicReconstruction-v1.rst @@ -0,0 +1,125 @@ + +.. algorithm:: + +.. summary:: + +.. alias:: + +.. properties:: + +Description +----------- + +.. warning:: This is an early, experimental version of the algorithm. + +The input and output workspaces are workspace groups where every +element is an image workspace. In the input workspace every image is a +2D projection from a different angle whereas in the output workspace +every image is a slice of a reconstructed 3D volume. The input +workspace must have one image workspace per projection from a +tomography imaging experiment. The output workspace will have one +image workspace for every slice of the output reconstructed volume. + +The following method is supported: FBP (following the TomoPy +implementation [TomoPy2014]). + +The implementation of TomoPy methods are based on the TomoPy source +code available from https://github.com/tomopy/tomopy/, which is: + +:: + + Copyright 2015. UChicago Argonne, LLC. This software was produced + under U.S. Government contract DE-AC02-06CH11357 for Argonne National + Laboratory (ANL), which is operated by UChicago Argonne, LLC for the + U.S. Department of Energy. The U.S. Government has rights to use, + reproduce, and distribute this software. NEITHER THE GOVERNMENT NOR + UChicago Argonne, LLC MAKES ANY WARRANTY, EXPRESS OR IMPLIED, OR + ASSUMES ANY LIABILITY FOR THE USE OF THIS SOFTWARE. If software is + modified to produce derivative works, such modified software should + be clearly marked, so as not to confuse it with the version available + from ANL. + + Additionally, redistribution and use in source and binary forms, with + or without modification, are permitted provided that the following + conditions are met: + + * Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + + * Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in + the documentation and/or other materials provided with the + distribution. + + * Neither the name of UChicago Argonne, LLC, Argonne National + Laboratory, ANL, the U.S. Government, nor the names of its + contributors may be used to endorse or promote products derived + from this software without specific prior written permission. + + THIS SOFTWARE IS PROVIDED BY UChicago Argonne, LLC AND CONTRIBUTORS + "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS + FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL UChicago + Argonne, LLC OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, + INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, + BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER + CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN + ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + POSSIBILITY OF SUCH DAMAGE. + +References: + +.. [TomoPy2014] Gursoy D, De Carlo F, Xiao X, + Jacobsen C. (2014). TomoPy: a framework for the analysis of + synchrotron tomographic data. J. Synchrotron Rad. 21. 1188-1193 + doi:10.1107/S1600577514013939 + + +Usage +----- + +**Example - ReconstructProjections** + +.. testcode:: ReconstructProjections + + # Note: you would load FITS images like this: + # wsg = LoadFITS(Filename='FITS_small_01.fits', LoadAsRectImg=1, OutputWorkspace='projections') + wsg_name = 'projections' + # Produce 16 projections with 32x32 pixels + projections = [] + unit_label=UnitFactory.create('Label') + unit_label.setLabel('width','cm') + for proj in range(0, 8): + wks_name = 'wks_proj_' + str(proj) + wks = CreateSampleWorkspace(NumBanks=32, BankPixelWidth=1, XMin=0, XMax=32, BinWidth=1, OutputWorkspace=wks_name) + wks.getAxis(0).setUnit('Label') + projections.append(wks) + wsg_proj = GroupWorkspaces(projections, OutputWorkspace=wsg_name) + wsg_reconstructed = ImggTomographicReconstruction(InputWorkspace=wsg_proj, CenterOfRotation=15) + rows = wsg_reconstructed.getItem(0).getNumberHistograms() + columns = wsg_reconstructed.getItem(0).blocksize() + print ("The output reconstruction has {0} slices of {1} by {2} pixels". + format(wsg_reconstructed.size(), rows, columns)) + slice_idx = 2 + coord_x = 8 + coord_y = 15 + print ("Value of pixel at coordinate ({0},{1}) in slice {2}: {3:.1f}". + format(coord_x, coord_y, slice_idx, + wsg_reconstructed.getItem(2).readY(coord_y)[coord_x])) + +.. testcleanup:: ReconstructProjections + + DeleteWorkspace(wsg_name) + +Output: + +.. testoutput:: ReconstructProjections + + The output reconstruction has 32 slices of 32 by 32 pixels + Value of pixel at coordinate (8,15) in slice 2: 2.4 + +.. categories:: + +.. sourcelink:: diff --git a/docs/source/release/v3.7.0/diffraction.rst b/docs/source/release/v3.7.0/diffraction.rst index be06a2a910fadf08cb1c2da07a8e5654f30edcee..c6187f92f77795633cdcdcbee37a6ae83b56c0eb 100644 --- a/docs/source/release/v3.7.0/diffraction.rst +++ b/docs/source/release/v3.7.0/diffraction.rst @@ -97,9 +97,16 @@ Imaging - The new algorithm :ref:`ImggAggregateWavelengths <algm-ImggAggregateWavelengths>` aggregates stacks of images from wavelength dependent data. +- The algorithm `ImggTomographicReconstruction + <algm-ImggTomographicReconstruction>` has been introduced. This is a + first experimental version that implements the Filtered + Back-Projection (FBP) reconstruction method using the FBP + implementation of the `TomoPy package + <http://www.aps.anl.gov/tomopy/>`_. - Images loaded as Mantid workspaces can now be saved into FITS files using the algorithm :ref:`SaveFITS <algm-SaveFITS>`. + Improvements in the tomographic reconstruction graphical user interface: - New capabilities added when visualizing stacks of images: diff --git a/docs/source/release/v3.7.0/framework.rst b/docs/source/release/v3.7.0/framework.rst index 7a7f122b47bef5f6f64d4e76328cc227a4a44ed2..846bbf2bb2e7787b3333074035f923cf02981310 100644 --- a/docs/source/release/v3.7.0/framework.rst +++ b/docs/source/release/v3.7.0/framework.rst @@ -28,9 +28,7 @@ New :ref:`GetDetOffsetsMultiPeaks <algm-GetDetOffsetsMultiPeaks>`, :ref:`CalibrateRectangularDetectors <algm-CalibrateRectangularDetectors>`, *et al* and minimizes the difference between the *DIFC* of the instrument and calibration by moving and rotating instrument components. - - :ref:`CorrectTOF <algm-CorrectTOF>` applies to the time-of-flight correction which considers the specified elastic peak position. - - :ref:`EnggFitDIFCFromPeaks <algm-AlignComponents>` fits GSAS calibration parameters (DIFA, DIFC, TZERO) from peaks fitted using :ref:`EnggFitPeaks <algm-EnggFitPeaks>`. @@ -42,6 +40,9 @@ New - :ref:`ImggAggregateWavelengths <algm-ImggAggregateWavelengths>` aggregates stacks of images from wavelength dependent imaging into one or more output bands. +- :ref:`ImggTomographicReconstruction + <algm-ImggTomographicReconstruction>` implements a method for 3D + tomographic reconstruction from projection images. - :ref:`SaveFITS <algm-SaveFITS>` saves images in FITS format. Renamed