LCOV - code coverage report
Current view: top level - elsa/projectors - JosephsMethod.cpp (source / functions) Hit Total Coverage
Test: coverage-all.lcov Lines: 109 122 89.3 %
Date: 2024-12-21 07:37:52 Functions: 31 64 48.4 %

          Line data    Source code
       1             : #include "JosephsMethod.h"
       2             : #include "Timer.h"
       3             : #include "DrivingDirectionTraversal.h"
       4             : #include "Error.h"
       5             : #include "TypeCasts.hpp"
       6             : 
       7             : #include <type_traits>
       8             : 
       9             : namespace elsa
      10             : {
      11             :     template <typename data_t>
      12             :     JosephsMethod<data_t>::JosephsMethod(const VolumeDescriptor& domainDescriptor,
      13             :                                          const DetectorDescriptor& rangeDescriptor)
      14             :         : base_type(domainDescriptor, rangeDescriptor)
      15          62 :     {
      16          62 :         auto dim = domainDescriptor.getNumberOfDimensions();
      17          62 :         if (dim != 2 && dim != 3) {
      18           0 :             throw InvalidArgumentError("JosephsMethod:only supporting 2d/3d operations");
      19           0 :         }
      20             : 
      21          62 :         if (dim != rangeDescriptor.getNumberOfDimensions()) {
      22           0 :             throw InvalidArgumentError("JosephsMethod: domain and range dimension need to match");
      23           0 :         }
      24             : 
      25          62 :         if (rangeDescriptor.getNumberOfGeometryPoses() == 0) {
      26           0 :             throw InvalidArgumentError("JosephsMethod: geometry list was empty");
      27           0 :         }
      28          62 :     }
      29             : 
      30             :     template <int dim>
      31             :     bool isInAABB(const IndexArray_t<dim>& indices, const IndexArray_t<dim>& aabbMin,
      32             :                   const IndexArray_t<dim>& aabbMax)
      33     2980466 :     {
      34     2980466 :         return (indices >= aabbMin && indices < aabbMax).all();
      35     2980466 :     }
      36             : 
      37             :     template <int dim>
      38             :     std::pair<RealArray_t<dim>, RealArray_t<dim>>
      39             :         getLinearInterpolationWeights(const RealArray_t<dim>& currentPos,
      40             :                                       const IndexArray_t<dim>& voxelFloor,
      41             :                                       const index_t drivingDirection)
      42     1817932 :     {
      43             :         // subtract 0.5 because the weight calculation assumes indices that refer to the center of
      44             :         // the voxels, while elsa works with the lower corners of the indices.
      45     1817932 :         RealArray_t<dim> complement_weight = currentPos - voxelFloor.template cast<real_t>() - 0.5f;
      46     1817932 :         RealArray_t<dim> weight = RealArray_t<dim>{1} - complement_weight;
      47             :         // set weights along drivingDirection to 1 so that the interpolation does not have to handle
      48             :         // the drivingDirection as a special case
      49     1817932 :         weight(drivingDirection) = 1;
      50     1817932 :         complement_weight(drivingDirection) = 1;
      51     1817932 :         return std::make_pair(weight, complement_weight);
      52     1817932 :     }
      53             : 
      54             :     template <int dim>
      55             :     index_t coord2Idx(IndexArray_t<dim> coord, IndexArray_t<dim> strides)
      56     3389241 :     {
      57     3389241 :         return (coord * strides).sum();
      58     3389241 :     }
      59             : 
      60             :     template <typename data_t, int dim, class Fn>
      61             :     void doInterpolation(const IndexArray_t<dim>& voxelFloor, const IndexArray_t<dim>& voxelCeil,
      62             :                          const RealArray_t<dim>& weight, const RealArray_t<dim>& complement_weight,
      63             :                          const IndexArray_t<dim>& aabbMin, const IndexArray_t<dim>& aabbMax, Fn fn)
      64     1873070 :     {
      65     3109528 :         auto clip = [](auto coord, auto lower, auto upper) { return coord.min(upper).max(lower); };
      66     1873070 :         IndexArray_t<dim> tempIndices;
      67     1873070 :         if constexpr (dim == 2) {
      68     3282687 :             auto interpol = [&](auto v1, auto v2, auto w1, auto w2) {
      69     3282687 :                 tempIndices << v1, v2;
      70             : 
      71     3282687 :                 bool is_in_aab = isInAABB(tempIndices, aabbMin, aabbMax);
      72     3282687 :                 tempIndices = clip(tempIndices, aabbMin, (aabbMax - 1));
      73     3282687 :                 auto weight = is_in_aab * w1 * w2;
      74     3282687 :                 fn(tempIndices, weight);
      75     3282687 :             };
      76             : 
      77          94 :             interpol(voxelFloor[0], voxelCeil[1], weight[0], complement_weight[1]);
      78          94 :             interpol(voxelCeil[0], voxelFloor[1], complement_weight[0], weight[1]);
      79          94 :         } else {
      80         375 :             auto interpol = [&](auto v1, auto v2, auto v3, auto w1, auto w2, auto w3) {
      81         375 :                 tempIndices << v1, v2, v3;
      82             : 
      83         375 :                 bool is_in_aab = isInAABB(tempIndices, aabbMin, aabbMax);
      84         375 :                 tempIndices = clip(tempIndices, aabbMin, (aabbMax - 1));
      85         375 :                 auto weight = is_in_aab * w1 * w2 * w3;
      86         375 :                 fn(tempIndices, weight);
      87         375 :             };
      88             : 
      89          94 :             interpol(voxelFloor[0], voxelFloor[1], voxelFloor[2], weight[0], weight[1], weight[2]);
      90          94 :             interpol(voxelFloor[0], voxelCeil[1], voxelCeil[2], weight[0], complement_weight[1],
      91          94 :                      complement_weight[2]);
      92          94 :             interpol(voxelCeil[0], voxelFloor[1], voxelCeil[2], complement_weight[0], weight[1],
      93          94 :                      complement_weight[2]);
      94          94 :             interpol(voxelCeil[0], voxelCeil[1], voxelFloor[2], complement_weight[0],
      95          94 :                      complement_weight[1], weight[2]);
      96          94 :         }
      97     1873070 :     }
      98             : 
      99             :     template <typename data_t>
     100             :     void JosephsMethod<data_t>::forward(const BoundingBox& aabb, const DataContainer<data_t>& x,
     101             :                                         DataContainer<data_t>& Ax) const
     102         210 :     {
     103         210 :         Timer timeguard("JosephsMethod", "apply");
     104         210 :         if (aabb.dim() == 2) {
     105         194 :             traverseVolume<false, 2>(aabb, x, Ax);
     106         194 :         } else if (aabb.dim() == 3) {
     107          16 :             traverseVolume<false, 3>(aabb, x, Ax);
     108          16 :         }
     109         210 :     }
     110             : 
     111             :     template <typename data_t>
     112             :     void JosephsMethod<data_t>::backward(const BoundingBox& aabb, const DataContainer<data_t>& y,
     113             :                                          DataContainer<data_t>& Aty) const
     114         144 :     {
     115         144 :         Timer timeguard("JosephsMethod", "applyAdjoint");
     116         144 :         if (aabb.dim() == 2) {
     117         135 :             traverseVolume<true, 2>(aabb, y, Aty);
     118         135 :         } else if (aabb.dim() == 3) {
     119           9 :             traverseVolume<true, 3>(aabb, y, Aty);
     120           9 :         }
     121         144 :     }
     122             : 
     123             :     template <typename data_t>
     124             :     JosephsMethod<data_t>* JosephsMethod<data_t>::_cloneImpl() const
     125          23 :     {
     126          23 :         return new self_type(downcast<VolumeDescriptor>(*this->_domainDescriptor),
     127          23 :                              downcast<DetectorDescriptor>(*this->_rangeDescriptor));
     128          23 :     }
     129             : 
     130             :     template <typename data_t>
     131             :     bool JosephsMethod<data_t>::_isEqual(const LinearOperator<data_t>& other) const
     132           4 :     {
     133           4 :         if (!LinearOperator<data_t>::isEqual(other))
     134           0 :             return false;
     135             : 
     136           4 :         auto otherJM = downcast_safe<JosephsMethod>(&other);
     137           4 :         return static_cast<bool>(otherJM);
     138           4 :     }
     139             : 
     140             :     template <typename data_t>
     141             :     template <bool adjoint, int dim>
     142             :     void JosephsMethod<data_t>::traverseVolume(const BoundingBox& aabb,
     143             :                                                const DataContainer<data_t>& vector,
     144             :                                                DataContainer<data_t>& result) const
     145         354 :     {
     146         354 :         if constexpr (adjoint)
     147         144 :             result = 0;
     148             : 
     149         354 :         const auto& domain = adjoint ? result.getDataDescriptor() : vector.getDataDescriptor();
     150         354 :         const auto& range = downcast<DetectorDescriptor>(adjoint ? vector.getDataDescriptor()
     151         354 :                                                                  : result.getDataDescriptor());
     152             : 
     153         354 :         const IndexArray_t<dim> strides = domain.getProductOfCoefficientsPerDimension();
     154         354 :         const auto sizeOfRange = range.getNumberOfCoefficients();
     155             : 
     156         354 :         const IndexArray_t<dim> aabbMin = aabb.min().template cast<index_t>();
     157         354 :         const IndexArray_t<dim> aabbMax = aabb.max().template cast<index_t>();
     158             : 
     159             :         // iterate over all rays
     160         354 : #pragma omp parallel for
     161         354 :         for (index_t ir = 0; ir < sizeOfRange; ir++) {
     162           0 :             const auto ray = range.computeRayFromDetectorCoord(ir);
     163             : 
     164             :             // --> setup traversal algorithm
     165             : 
     166           0 :             DrivingDirectionTraversal<dim> traverse(aabb, ray);
     167           0 :             const index_t drivingDirection = traverse.getDrivingDirection();
     168           0 :             const data_t intersection = traverse.getIntersectionLength();
     169             : 
     170           0 :             if constexpr (!adjoint)
     171       99446 :                 result[ir] = 0;
     172             : 
     173             :             // Make steps through the volume
     174     1811078 :             while (traverse.isInBoundingBox()) {
     175     1811078 :                 const IndexArray_t<dim> voxelFloor = traverse.getCurrentVoxelFloor();
     176     1811078 :                 const IndexArray_t<dim> voxelCeil = traverse.getCurrentVoxelCeil();
     177     1811078 :                 const auto [weight, complement_weight] = getLinearInterpolationWeights(
     178     1811078 :                     traverse.getCurrentPos(), voxelFloor, drivingDirection);
     179             : 
     180     1811078 :                 if constexpr (adjoint) {
     181     1041747 :                     doInterpolation<data_t>(voxelFloor, voxelCeil, weight, complement_weight,
     182     1450722 :                                             aabbMin, aabbMax, [&](const auto& coord, auto wght) {
     183     1450722 : #pragma omp atomic
     184     1450722 :                                                 result[coord2Idx(coord, strides)] +=
     185     1450722 :                                                     vector[ir] * intersection * wght;
     186     1450722 :                                             });
     187     1041747 :                 } else {
     188     1041747 :                     doInterpolation<data_t>(voxelFloor, voxelCeil, weight, complement_weight,
     189     1957719 :                                             aabbMin, aabbMax, [&](const auto& coord, auto wght) {
     190     1957719 :                                                 result[ir] += vector[coord2Idx(coord, strides)]
     191     1957719 :                                                               * intersection * wght;
     192     1957719 :                                             });
     193     1041747 :                 }
     194             :                 // update Traverse
     195     1811078 :                 traverse.updateTraverse();
     196     1811078 :             }
     197           0 :         }
     198         354 :     }
     199             : 
     200             :     // ------------------------------------------
     201             :     // explicit template instantiation
     202             :     template class JosephsMethod<float>;
     203             :     template class JosephsMethod<double>;
     204             : 
     205             : } // namespace elsa

Generated by: LCOV version 1.14