Line data Source code
1 : #include "BaseCircleTrajectoryGenerator.h" 2 : #include "Logger.h" 3 : #include "TypeCasts.hpp" 4 : #include "VolumeDescriptor.h" 5 : 6 : #include <algorithm> 7 : #include <optional> 8 : #include <stdexcept> 9 : 10 : namespace elsa 11 : { 12 : std::tuple<IndexVector_t, RealVector_t, std::vector<Geometry>> 13 : BaseCircleTrajectoryGenerator::createTrajectoryData( 14 : const std::vector<real_t>& thetas, const DataDescriptor& volumeDescriptor, 15 : real_t sourceToCenter, real_t centerToDetector, 16 : std::optional<RealVector_t> principalPointOffset, 17 : std::optional<RealVector_t> centerOfRotOffset, 18 : std::optional<IndexVector_t> detectorSize, std::optional<RealVector_t> detectorSpacing) 19 20 : { 20 : // pull in geometry namespace, to reduce cluttering 21 20 : using namespace geometry; 22 : 23 : // sanity check 24 20 : const auto dim = volumeDescriptor.getNumberOfDimensions(); 25 : 26 20 : if (dim < 2 || dim > 3) 27 0 : throw InvalidArgumentError("CircleTrajectoryGenerator: can only handle 2d/3d"); 28 : 29 20 : const auto numberOfPoses = asSigned(thetas.size()); 30 20 : const auto arc = *std::max_element(thetas.begin(), thetas.end()); 31 20 : Logger::get("CircleTrajectoryGenerator") 32 20 : ->info("creating {}D trajectory with {} poses in an {} degree arc", dim, numberOfPoses, 33 20 : arc); 34 : 35 : // Calculate size and spacing for each geometry pose using a IIFE 36 20 : const auto [coeffs, spacing] = calculateSizeAndSpacingPerGeometry( 37 20 : volumeDescriptor, numberOfPoses, detectorSize, detectorSpacing); 38 : 39 : // Create vector and reserve the necessary size, minor optimization such that no new 40 : // allocations are necessary in the loop 41 20 : std::vector<Geometry> geometryList; 42 20 : geometryList.reserve(asUnsigned(numberOfPoses)); 43 : 44 1620 : for (auto degree : thetas) { 45 1620 : const auto angle = Degree{degree}.to_radian(); 46 1620 : if (dim == 2) { 47 : // Use emplace_back, then no copy is created 48 1108 : geometryList.emplace_back( 49 1108 : SourceToCenterOfRotation{sourceToCenter}, 50 1108 : CenterOfRotationToDetector{centerToDetector}, Radian{angle}, 51 1108 : VolumeData2D{volumeDescriptor.getSpacingPerDimension(), 52 1108 : volumeDescriptor.getLocationOfOrigin()}, 53 1108 : SinogramData2D{Size2D{coeffs}, Spacing2D{spacing}}, 54 1108 : principalPointOffset ? PrincipalPointOffset{principalPointOffset.value()[0]} 55 1108 : : PrincipalPointOffset{0}, 56 1108 : centerOfRotOffset ? RotationOffset2D{centerOfRotOffset.value()} 57 1108 : : RotationOffset2D{0, 0}); 58 1108 : } else { 59 512 : geometryList.emplace_back( 60 512 : SourceToCenterOfRotation{sourceToCenter}, 61 512 : CenterOfRotationToDetector{centerToDetector}, 62 512 : VolumeData3D{volumeDescriptor.getSpacingPerDimension(), 63 512 : volumeDescriptor.getLocationOfOrigin()}, 64 512 : SinogramData3D{Size3D{coeffs}, Spacing3D{spacing}}, 65 512 : RotationAngles3D{Radian{angle}, Radian{0}, Radian{0}}, 66 512 : principalPointOffset ? PrincipalPointOffset2D{principalPointOffset.value()} 67 512 : : PrincipalPointOffset2D{0, 0}, 68 512 : centerOfRotOffset ? RotationOffset3D{centerOfRotOffset.value()} 69 512 : : RotationOffset3D{0, 0, 0}); 70 512 : } 71 1620 : } 72 : 73 20 : return std::make_tuple(std::move(coeffs), std::move(spacing), std::move(geometryList)); 74 20 : } 75 : } // namespace elsa