otbZipIterator.h 13.3 KB
Newer Older
1
/*
Luc Hermitte's avatar
Luc Hermitte committed
2
 * Copyright (C) 2005-2020 Centre National d'Etudes Spatiales (CNES)
3 4 5
 *
 * This file is part of Orfeo Toolbox
 *
Luc Hermitte's avatar
Luc Hermitte committed
6
 *     https://www.orfeo-toolbox.org/
7
 *
Luc Hermitte's avatar
Luc Hermitte committed
8
 * Licensed under the Apache License, Version 2.0 (the "License");
9 10 11
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
Luc Hermitte's avatar
Luc Hermitte committed
12
 *     http://www.apache.org/licenses/LICENSE-2.0
13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

#ifndef otbZipIterator_h
#define otbZipIterator_h

#include "otbSpan.h"
#include "itkMacro.h"
#include <type_traits>
#include <vector>
#include <cassert>

namespace otb
{
namespace internals
{

struct ConstTag {};
struct MutableTag {};

/**
 * Wrapper to present list of iterators as a single iterator.
 *
 * \invariant This class shall not be inherited.
 * \invariant All sub iterators are always synchronised (same region, size...)
 * \invariant `!m_iterators.empty()`
 * \author Luc Hermitte (CS Group)
 * \copyright CNES
 * \ingroup OTBCommon
 */
template <typename TImageIterator, typename ConstOrMutable>
class ZipIterator
{
public:
  /**\name ITK Constants and Typedefs */
  //@{
  /** Iterator type for zipped iterators. */
  using ImageIteratorType     = TImageIterator;

  /** Image type alias support */
  using ImageType             = typename TImageIterator::ImageType;

  /** Dimension of the image the iterator walks.  This constant is needed so
   * functions that are templated over image iterator type (as opposed to
   * being templated over pixel type and dimension) can have compile time
   * access to the dimension of the image that the iterator walks. */
  static constexpr unsigned int ImageIteratorDimension = ImageIteratorType::ImageIteratorDimension;

  using Self                  = ZipIterator;

  /** Run-time type information (and related methods). */
  itkTypeMacroNoParent(ZipIterator);

  /** Index type alias support */
  using IndexType             = typename ImageIteratorType::IndexType;

  /** Size type alias support */
  using SizeType              = typename ImageIteratorType::SizeType;

  /** Offset type alias support */
  using OffsetType            = typename ImageIteratorType::OffsetType;

  /** Region type alias support */
  using RegionType            = typename ImageIteratorType::RegionType;

  /** PixelContainer type alias support. Used to refer to the container for
   * the pixel data. While this was already typdef'ed in the superclass
   * it needs to be redone here for this subclass to compile properly with gcc. */
  using PixelContainer        = typename ImageIteratorType::PixelContainer;
  using PixelContainerPointer = typename PixelContainer::Pointer;

  /** Internal Pixel Type */
  using InternalPixelType     = typename ImageIteratorType::InternalPixelType;

  /** External Pixel Type */
  using PixelType             = typename ImageIteratorType::PixelType;

  /**  Accessor type that convert data between internal and external
   *  representations. */
  using AccessorType          = typename ImageIteratorType::AccessorType;
  using AccessorFunctorType   = typename ImageIteratorType::AccessorFunctorType;
  //@}

  /**\name Constructions & Destruction
   *
   * This class follows the rule of 0/5
   */
  //@{
  ZipIterator           ()                   = default;
  ~ZipIterator          ()                   = default;
  ZipIterator           (ZipIterator const&) = default;
  ZipIterator           (ZipIterator     &&) = default;
  ZipIterator& operator=(ZipIterator const&) = default;
  ZipIterator& operator=(ZipIterator     &&) = default;

Cédric Traizet's avatar
Cédric Traizet committed
113
  /** Conversion constructor.
114 115 116 117 118 119 120 121
   * Converts from mutable to const iterator
   * \see https://quuxplusone.github.io/blog/2018/12/01/const-iterator-antipatterns/
   */
  template <bool IsConst_ = std::is_same<ConstOrMutable, ConstTag>::value, class = std::enable_if<IsConst_>>
  ZipIterator(ZipIterator<ImageIteratorType, MutableTag> const& rhs)
    : m_iterators(rhs.m_iterators())
    {}

Cédric Traizet's avatar
Cédric Traizet committed
122
  /** Conversion move constructor.
123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 300 301 302 303 304 305 306 307 308 309 310 311 312 313 314 315 316 317 318 319 320 321 322 323 324 325 326 327 328 329 330 331 332 333 334 335 336 337 338 339 340 341 342 343 344 345 346 347 348 349 350 351 352 353 354 355 356 357 358 359 360 361 362 363 364 365 366 367 368 369 370 371 372 373 374 375 376 377 378 379 380 381 382 383 384 385 386 387 388 389 390 391 392 393 394 395 396 397 398 399 400 401 402 403 404 405 406 407 408 409 410 411 412 413 414 415 416 417 418
   * Move converts from mutable to const iterator
   * \see https://quuxplusone.github.io/blog/2018/12/01/const-iterator-antipatterns/
   */
  template <bool IsConst_ = std::is_same<ConstOrMutable, ConstTag>::value, class = std::enable_if<IsConst_>>
  ZipIterator(ZipIterator<ImageIteratorType, MutableTag> && rhs)
    : m_iterators(move(rhs.m_iterators()))
    {}

  /**
   * Init Constructor.
   * Constructs a `ZipIterator` from a list of Images and a Region.
   * \param[in,out] images  List of images
   * \param[in]     region  Region to iterate over
   * \pre There should be at least one image
   */
  ZipIterator(Span<ImageType * const> images, RegionType const& region)
  {
    assert(! images.empty());
    m_iterators.reserve(images.size());

    for (auto & im: images)
      m_iterators.emplace_back(im, region);
  }

  // static_assert(std::is_copy_constructible<Self>::value, "Requires copy construction");
  // static_assert(std::is_trivially_copy_constructible<Self>::value, "Requires trivial copy construction");
  //@}

  /**\name Comparison */
  //@{
  friend bool operator==(ZipIterator const& lhs, ZipIterator const& rhs)
  {
    assert(!lhs.m_iterators.empty());
    assert(lhs.m_iterators.size() == rhs.m_iterators.size());

    return lhs.m_iterators.front() == rhs.m_iterators.front();
  }
  friend bool operator!=(ZipIterator const& lhs, ZipIterator const& rhs)
  { return ! (lhs == rhs); }

  friend bool operator<=(ZipIterator const& lhs, ZipIterator const& rhs)
  {
    assert(!lhs.m_iterators.empty());
    assert(lhs.m_iterators.size() == rhs.m_iterators.size());

    return lhs.m_iterators.front() <= rhs.m_iterators.front();
  }
  friend bool operator<(ZipIterator const& lhs, ZipIterator const& rhs)
  {
    assert(!lhs.m_iterators.empty());
    assert(lhs.m_iterators.size() == rhs.m_iterators.size());

    return lhs.m_iterators.front() < rhs.m_iterators.front();
  }
  friend bool operator>=(ZipIterator const& lhs, ZipIterator const& rhs)
  { return ! (lhs < rhs); }
  friend bool operator>(ZipIterator const& lhs, ZipIterator const& rhs)
  { return ! (lhs <= rhs); }
  //@}

  /**\name ITK iterator interface */
  //@{
  // What about GetIndex() and SetIndex ?
  /** Fetch the region iterated by the iterator. */
  auto const& GetRegion() const
  {
    assert(!m_iterators.empty());
    return m_iterators.front().GetRegion();
  }

  /** Set the region iterated by the iterator. */
  void SetRegion(RegionType const& region)
  {
    for (auto & it : m_iterators)
      it.SetRegion(region);
  }

  /** Moves the iterator to the begin of the region iterated. */
  Self& GoToBegin() {
    for (auto & it : m_iterators)
      it.GoToBegin();
    return *this;
  }
  /** Moves the iterator to the end of the region iterated. */
  Self& GoToEnd() {
    for (auto & it : m_iterators)
      it.GoToEnd();
    return *this;
  }

  /** Tells whether the iterator is at the begin of the region iterated. */
  bool IsAtBegin() const {
    assert(!m_iterators.empty());
    return m_iterators.front().IsAtBegin();
  }

  /** Tells the iterator is at the end of the region iterated. */
  bool IsAtEnd() const {
    assert(!m_iterators.empty());
    return m_iterators.front().IsAtEnd();
  }

  /** Pre-increment the iterator.
   * As post-increment is less efficient, it hasn't been provided.
   */
  Self& operator++() {
    assert(!IsAtEnd());
    for (auto & it : m_iterators)
      ++it;
    return *this;
  }
  /** Removed post-increment operator.
   * Please use the  preincrement operator!
   */
  Self operator++(int) = delete;

  /** Internal typedef to the type holding the list of ITK iterators. */
  using ImageIteratorList_t = std::vector<ImageIteratorType>;
  /** Internal Pixel Proxy Type returned by `ZipIterator`.
   * \author Luc Hermitte (CS Group)
   * \copyright CNES
   * \ingroup OTBCommon
   */
  struct PixelListProxy
  {
    /** Init constructor.
     * @param[in] iterators List of ITK iterators
     */
    explicit PixelListProxy(ImageIteratorList_t const& iterators) noexcept
      : m_iterators(iterators)
      {}
    bool empty()   const noexcept {return m_iterators.empty();}
    auto size()    const noexcept {return m_iterators.size();}

    /** Internal C++ iterator over the components of the Pixel Proxy.
     * \author Luc Hermitte (CS Group)
     * \copyright CNES
     * \ingroup OTBCommon
     */
    struct iterator__
    {
      using difference_type   = typename ImageIteratorList_t::difference_type;
      using value_type        = decltype(typename ImageIteratorList_t::const_iterator{}->Get());
      using pointer           = value_type*;
      using reference         = value_type&;
      using iterator_category = std::forward_iterator_tag;

      explicit iterator__(typename ImageIteratorList_t::const_iterator ref)
        : reference_to_value(ref){}
      friend bool operator==(iterator__ const& lhs, iterator__ const& rhs) noexcept
      { return lhs.reference_to_value == rhs.reference_to_value;}
      friend bool operator!=(iterator__ const& lhs, iterator__ const& rhs) noexcept
      { return ! (lhs == rhs);}
      iterator__ & operator++() noexcept {
        ++reference_to_value;
        return *this;
      }
      iterator__ & operator--() noexcept {
        --reference_to_value;
        return *this;
      }
      iterator__ operator+(std::ptrdiff_t offset) const noexcept{
        return iterator__{reference_to_value + offset};
      }
      iterator__ operator-(std::ptrdiff_t offset) const noexcept{
        return iterator__{reference_to_value - offset};
      }
      decltype(auto) operator*() const {
        return reference_to_value->Get();
      }

    private:
      typename ImageIteratorList_t::const_iterator reference_to_value;
    };

    auto begin()         noexcept { return iterator__(m_iterators.begin()); }
    auto end()           noexcept { return iterator__{m_iterators.end()}; }
    auto begin()   const noexcept { return iterator__{m_iterators.begin()}; }
    auto end()     const noexcept { return iterator__{m_iterators.end()}; }
    auto cbegin()  const noexcept { return iterator__{m_iterators.cbegin()}; }
    auto cend()    const noexcept { return iterator__{m_iterators.cend()}; }

    decltype(auto) operator[](std::size_t idx) const {
      assert(idx < size());
      return m_iterators[idx].Get();
    }
    decltype(auto) operator[](std::size_t idx)       {
      assert(idx < size());
      return m_iterators[idx].Get();
    }

    decltype(auto) front() const {
      assert(!empty());
      return m_iterators.front().Get();
    }
    decltype(auto) front()       {
      assert(!empty());
      return m_iterators.front().Get();
    }
    decltype(auto) back() const {
      assert(!empty());
      return m_iterators.back().Get();
    }
    decltype(auto) back()       {
      assert(!empty());
      return m_iterators.back().Get();
    }
  private:
    ImageIteratorList_t const& m_iterators;
  };

  /** Fetches the value of the current pixel.
   * \return an iterable proxy over the pixel made of all images.
   */
  PixelListProxy Get() const {
    return PixelListProxy{m_iterators};
  }

  //@}

  /**\name Mutable Iterator Interface */
  //@{
  template <typename MultiCompPixelType>
  void Set(MultiCompPixelType const& p)
  {
    assert(p.size() == m_iterators.size());
    for (std::size_t i = 0; i!=m_iterators.size(); ++i)
    {
      m_iterators[i].Set(p[i]);
    }
  }
  // PixelType & Value(); -- cannot be defined and still preserve direct access
  // to memory => we don't provide it.

  // ImageType * GetImages();
  //@}

  /**\name ScanLine Iterator Interface */
  //@{
  /** Moves iterator to next line. */
  Self& NextLine() {
    for (auto & it : m_iterators)
      it.NextLine();
    return *this;
  }
  /** Moves iterator to the beginning of the current line. */
  Self& GoToBeginOfLine() {
    for (auto & it : m_iterators)
      it.GoToBeginOfLine();
    return *this;
  }
  /** Moves iterator to the end of the current line. */
  Self& GoToEndOfLine() {
    for (auto & it : m_iterators)
      it.GoToEndOfLine();
    return *this;
  }
  /** Tells whether the iterator is a the end of a line.
   * \pre `!m_iterators.empty()`
   */
  bool IsAtEndOfLine() const {
    assert(!m_iterators.empty());
    // Const qualifier has been added to ScanLineIterator::IsAtEndOfLine in ITK
    // 5.1 => Use const_cast in the mean time...
    return const_cast<typename ImageIteratorList_t::value_type &>(m_iterators.front()).IsAtEndOfLine();
  }
  //@}

private:
  ImageIteratorList_t m_iterators;
};

} // otb::internal namespace

/** Typedef for a ZipIterator.
 * ZipIterator presents a single iterator over a list of image iterators.
 * \author Luc Hermitte (CS Group)
 * \copyright CNES
 * \ingroup OTBCommon
 * \see `otb::internals::ZipIterator<>`
 */
template <typename TImageIterator>
using ZipIterator      = internals::ZipIterator<TImageIterator, internals::MutableTag>;
/** Typedef for a ZipConstIterator.
 * ZipConstIterator presents a single iterator over a list of const image iterators.
 * \author Luc Hermitte (CS Group)
 * \copyright CNES
 * \ingroup OTBCommon
 * \see `otb::internals::ZipIterator<>`
 */
template <typename TImageIterator>
using ZipConstIterator = internals::ZipIterator<TImageIterator, internals::ConstTag>;

} // otb namespace

#endif  // otbZipIterator_h