LCOV - code coverage report
Current view: top level - elsa/projectors - SubsetSampler.cpp (source / functions) Hit Total Coverage
Test: coverage-all.lcov Lines: 96 105 91.4 %
Date: 2024-05-16 04:22:26 Functions: 9 36 25.0 %

          Line data    Source code
       1             : #include <set>
       2             : #include <numeric>
       3             : #include "SubsetSampler.h"
       4             : #include "PartitionDescriptor.h"
       5             : 
       6             : namespace elsa
       7             : {
       8             :     template <typename DetectorDescriptor_t, typename data_t>
       9             :     SubsetSampler<DetectorDescriptor_t, data_t>::SubsetSampler(
      10             :         const VolumeDescriptor& volumeDescriptor, const DetectorDescriptor_t& detectorDescriptor,
      11             :         index_t nSubsets, SamplingStrategy samplingStrategy)
      12             :         : _indexMapping(static_cast<std::size_t>(nSubsets)),
      13             :           _volumeDescriptor(volumeDescriptor),
      14             :           _fullDetectorDescriptor(detectorDescriptor),
      15             :           _nSubsets{nSubsets}
      16          12 :     {
      17          12 :         if (nSubsets <= 1) {
      18           0 :             throw std::invalid_argument("SubsetSampler: nSubsets must be >= 2");
      19           0 :         }
      20             : 
      21             :         // the mapping of data indices to subsets
      22             : 
      23          12 :         const auto numCoeffsPerDim = detectorDescriptor.getNumberOfCoefficientsPerDimension();
      24          12 :         const index_t nDimensions = detectorDescriptor.getNumberOfDimensions();
      25          12 :         const auto numElements = numCoeffsPerDim[nDimensions - 1];
      26          12 :         if (samplingStrategy == SamplingStrategy::ROUND_ROBIN) {
      27           6 :             std::vector<index_t> indices(static_cast<std::size_t>(numElements));
      28           6 :             std::iota(indices.begin(), indices.end(), 0);
      29           6 :             _indexMapping = splitRoundRobin(indices, _nSubsets);
      30           6 :         } else if (samplingStrategy == SamplingStrategy::ROTATIONAL_CLUSTERING) {
      31           6 :             _indexMapping = splitRotationalClustering(detectorDescriptor, _nSubsets);
      32           6 :         } else {
      33           0 :             throw std::invalid_argument("SubsetSampler: unsupported sampling strategy");
      34           0 :         }
      35             : 
      36             :         // create the detector descriptors that correspond to each subset
      37          48 :         for (const auto& blockIndices : _indexMapping) {
      38          48 :             std::vector<Geometry> geometry;
      39          48 :             geometry.reserve(blockIndices.size());
      40         216 :             for (auto index : blockIndices) {
      41         216 :                 geometry.emplace_back(detectorDescriptor.getGeometryAt(index));
      42         216 :             }
      43          48 :             IndexVector_t numOfCoeffsPerDim =
      44          48 :                 detectorDescriptor.getNumberOfCoefficientsPerDimension();
      45          48 :             numOfCoeffsPerDim[numOfCoeffsPerDim.size() - 1] =
      46          48 :                 static_cast<index_t>(blockIndices.size());
      47             : 
      48          48 :             _detectorDescriptors.emplace_back(DetectorDescriptor_t(numOfCoeffsPerDim, geometry));
      49          48 :         }
      50          12 :     }
      51             : 
      52             :     template <typename DetectorDescriptor_t, typename data_t>
      53             :     std::vector<std::vector<index_t>> SubsetSampler<DetectorDescriptor_t, data_t>::splitRoundRobin(
      54             :         const std::vector<index_t>& indices, index_t nSubsets)
      55          18 :     {
      56          18 :         std::vector<std::vector<index_t>> subsetIndices(static_cast<std::size_t>(nSubsets));
      57             : 
      58             :         // determine the mapping of indices to subsets
      59         426 :         for (std::size_t i = 0; i < indices.size(); ++i) {
      60         408 :             const auto subset = i % static_cast<std::size_t>(nSubsets);
      61         408 :             subsetIndices[subset].template emplace_back(indices[i]);
      62         408 :         }
      63             : 
      64          18 :         return subsetIndices;
      65          18 :     }
      66             : 
      67             :     template <typename DetectorDescriptor_t, typename data_t>
      68             :     std::vector<std::vector<index_t>>
      69             :         SubsetSampler<DetectorDescriptor_t, data_t>::splitRotationalClustering(
      70             :             const DetectorDescriptor_t& detectorDescriptor, index_t nSubsets)
      71           8 :     {
      72             : 
      73           8 :         const auto numCoeffsPerDim = detectorDescriptor.getNumberOfCoefficientsPerDimension();
      74           8 :         const index_t nDimensions = detectorDescriptor.getNumberOfDimensions();
      75           8 :         const auto numElements = numCoeffsPerDim[nDimensions - 1];
      76           8 :         std::vector<index_t> indices(static_cast<std::size_t>(numElements));
      77           8 :         std::iota(indices.begin(), indices.end(), 0);
      78           8 :         const auto geometry = detectorDescriptor.getGeometry();
      79             : 
      80             :         // angle between two rotation matrices used as a distance measure
      81        1660 :         auto dist = [nDimensions](auto& g1, auto& g2) {
      82        1660 :             const auto& r1 = g1.getRotationMatrix();
      83        1660 :             const auto& r2 = g2.getRotationMatrix();
      84        1660 :             auto product = r1 * r2.transpose();
      85        1660 :             if (nDimensions == 2) { // the 2D case
      86         938 :                 return static_cast<double>(std::atan2(product(1, 0), product(0, 0)));
      87         938 :             } else { // the 3D case
      88         722 :                 return std::acos((product.trace() - 1.0) / 2.0);
      89         722 :             }
      90        1660 :         };
      91             : 
      92           8 :         const auto first = geometry.front();
      93           8 :         std::sort(std::begin(indices), std::end(indices),
      94         830 :                   [dist, first, &geometry](auto lhs, auto rhs) {
      95         830 :                       return dist(first, geometry[static_cast<std::size_t>(lhs)])
      96         830 :                              < dist(first, geometry[static_cast<std::size_t>(rhs)]);
      97         830 :                   });
      98             : 
      99           8 :         return splitRoundRobin(indices, nSubsets);
     100           8 :     }
     101             : 
     102             :     template <typename DetectorDescriptor_t, typename data_t>
     103             :     SubsetSampler<DetectorDescriptor_t, data_t>::SubsetSampler(
     104             :         const SubsetSampler<DetectorDescriptor_t, data_t>& other)
     105             :         : _indexMapping{other._indexMapping},
     106             :           _volumeDescriptor(other._volumeDescriptor),
     107             :           _fullDetectorDescriptor(other._fullDetectorDescriptor),
     108             :           _nSubsets{other._nSubsets}
     109           4 :     {
     110          16 :         for (const auto& detectorDescriptor : other._detectorDescriptors) {
     111          16 :             _detectorDescriptors.emplace_back(detectorDescriptor);
     112          16 :         }
     113           4 :     }
     114             : 
     115             :     template <typename DetectorDescriptor_t, typename data_t>
     116             :     DataContainer<data_t> SubsetSampler<DetectorDescriptor_t, data_t>::getPartitionedData(
     117             :         const DataContainer<data_t>& sinogram)
     118           8 :     {
     119             :         // save the number of entries per subset
     120           8 :         IndexVector_t slicesInBlock(_indexMapping.size());
     121          40 :         for (unsigned long i = 0; i < _indexMapping.size(); i++) {
     122          32 :             slicesInBlock[static_cast<index_t>(i)] = static_cast<index_t>(_indexMapping[i].size());
     123          32 :         }
     124           8 :         PartitionDescriptor dataDescriptor(sinogram.getDataDescriptor(), slicesInBlock);
     125             : 
     126           8 :         const auto numCoeffsPerDim =
     127           8 :             sinogram.getDataDescriptor().getNumberOfCoefficientsPerDimension();
     128           8 :         auto partitionedData = DataContainer<data_t>(dataDescriptor);
     129             : 
     130             :         // resort the actual measurement partitionedData
     131           8 :         index_t coeffsPerRow = numCoeffsPerDim.head(numCoeffsPerDim.size() - 1).prod();
     132          40 :         for (index_t i = 0; i < _nSubsets; i++) {
     133             :             // the indices of the partitionedData rows belonging to this subset
     134          32 :             std::vector<index_t> indices = _indexMapping[static_cast<std::size_t>(i)];
     135             : 
     136          32 :             auto block = partitionedData.getBlock(i);
     137             : 
     138         176 :             for (std::size_t j = 0; j < indices.size(); j++) {
     139        9648 :                 for (int k = 0; k < coeffsPerRow; k++) {
     140        9504 :                     block[static_cast<index_t>(j) * coeffsPerRow + k] =
     141        9504 :                         sinogram[indices[j] * coeffsPerRow + k];
     142        9504 :                 }
     143         144 :             }
     144          32 :         }
     145           8 :         return partitionedData;
     146           8 :     }
     147             : 
     148             :     template <typename DetectorDescriptor_t, typename data_t>
     149             :     SubsetSampler<DetectorDescriptor_t, data_t>*
     150             :         SubsetSampler<DetectorDescriptor_t, data_t>::cloneImpl() const
     151           4 :     {
     152           4 :         return new SubsetSampler<DetectorDescriptor_t, data_t>(*this);
     153           4 :     }
     154             : 
     155             :     template <typename DetectorDescriptor_t, typename data_t>
     156             :     bool SubsetSampler<DetectorDescriptor_t, data_t>::isEqual(
     157             :         const SubsetSampler<DetectorDescriptor_t, data_t>& other) const
     158           4 :     {
     159           4 :         if (typeid(*this) != typeid(other))
     160           0 :             return false;
     161             : 
     162           4 :         if (_indexMapping != other._indexMapping)
     163           0 :             return false;
     164             : 
     165           4 :         if (_volumeDescriptor != other._volumeDescriptor)
     166           0 :             return false;
     167             : 
     168           4 :         if (_fullDetectorDescriptor != other._fullDetectorDescriptor)
     169           0 :             return false;
     170             : 
     171           4 :         if (_nSubsets != other._nSubsets)
     172           0 :             return false;
     173             : 
     174             :         // we do not need to check if the vector of detector descriptors is equal as this is
     175             :         // implied by the equality of _data in combination with the full detector descriptor
     176             : 
     177           4 :         return true;
     178           4 :     }
     179             : 
     180             :     // ------------------------------------------
     181             :     // explicit template instantiation
     182             :     template class SubsetSampler<PlanarDetectorDescriptor, float>;
     183             :     template class SubsetSampler<PlanarDetectorDescriptor, double>;
     184             :     template class SubsetSampler<PlanarDetectorDescriptor, complex<float>>;
     185             :     template class SubsetSampler<PlanarDetectorDescriptor, complex<double>>;
     186             : } // namespace elsa

Generated by: LCOV version 1.14