| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323 |
- /***************************************************************************
- * Copyright (c) Johan Mabille, Sylvain Corlay and Wolf Vollprecht *
- * Copyright (c) QuantStack *
- * *
- * Distributed under the terms of the BSD 3-Clause License. *
- * *
- * The full license is in the file LICENSE, distributed with this software. *
- ****************************************************************************/
- #ifndef XTENSOR_PAD_HPP
- #define XTENSOR_PAD_HPP
- #include "xarray.hpp"
- #include "xstrided_view.hpp"
- #include "xtensor.hpp"
- #include "xview.hpp"
- using namespace xt::placeholders; // to enable _ syntax
- namespace xt
- {
- /**
- * @brief Defines different algorithms to be used in ``xt::pad``:
- * - ``constant``: Pads with a constant value.
- * - ``symmetric``: Pads with the reflection of the vector mirrored along the edge of the array.
- * - ``reflect``: Pads with the reflection of the vector mirrored on the first and last values
- * of the vector along each axis.
- * - ``wrap``: Pads with the wrap of the vector along the axis. The first values are used to pad
- * the end and the end values are used to pad the beginning.
- * - ``periodic`` : ``== wrap`` (pads with periodic repetitions of the vector).
- *
- * OpenCV to xtensor:
- * - ``BORDER_CONSTANT == constant``
- * - ``BORDER_REFLECT == symmetric``
- * - ``BORDER_REFLECT_101 == reflect``
- * - ``BORDER_WRAP == wrap``
- */
- enum class pad_mode
- {
- constant,
- symmetric,
- reflect,
- wrap,
- periodic,
- edge
- };
- namespace detail
- {
- template <class S, class T>
- inline bool check_pad_width(const std::vector<std::vector<S>>& pad_width, const T& shape)
- {
- if (pad_width.size() != shape.size())
- {
- return false;
- }
- return true;
- }
- }
- /**
- * @brief Pad an array.
- *
- * @param e The array.
- * @param pad_width Number of values padded to the edges of each axis:
- * `{{before_1, after_1}, ..., {before_N, after_N}}`.
- * @param mode The type of algorithm to use. [default: `xt::pad_mode::constant`].
- * @param constant_value The value to set the padded values for each axis
- * (used in `xt::pad_mode::constant`).
- * @return The padded array.
- */
- template <class E, class S = typename std::decay_t<E>::size_type, class V = typename std::decay_t<E>::value_type>
- inline auto
- pad(E&& e,
- const std::vector<std::vector<S>>& pad_width,
- pad_mode mode = pad_mode::constant,
- V constant_value = 0)
- {
- XTENSOR_ASSERT(detail::check_pad_width(pad_width, e.shape()));
- using size_type = typename std::decay_t<E>::size_type;
- using return_type = temporary_type_t<E>;
- // place the original array in the center
- auto new_shape = e.shape();
- xt::xstrided_slice_vector sv;
- sv.reserve(e.shape().size());
- for (size_type axis = 0; axis < e.shape().size(); ++axis)
- {
- size_type nb = static_cast<size_type>(pad_width[axis][0]);
- size_type ne = static_cast<size_type>(pad_width[axis][1]);
- size_type ns = nb + e.shape(axis) + ne;
- new_shape[axis] = ns;
- sv.push_back(xt::range(nb, nb + e.shape(axis)));
- }
- if (mode == pad_mode::constant)
- {
- return_type out(new_shape, constant_value);
- xt::strided_view(out, sv) = e;
- return out;
- }
- return_type out(new_shape);
- xt::strided_view(out, sv) = e;
- // construct padded regions based on original image
- xt::xstrided_slice_vector svs(e.shape().size(), xt::all());
- xt::xstrided_slice_vector svt(e.shape().size(), xt::all());
- for (size_type axis = 0; axis < e.shape().size(); ++axis)
- {
- size_type nb = static_cast<size_type>(pad_width[axis][0]);
- size_type ne = static_cast<size_type>(pad_width[axis][1]);
- if (nb > static_cast<size_type>(0))
- {
- svt[axis] = xt::range(0, nb);
- if (mode == pad_mode::wrap || mode == pad_mode::periodic)
- {
- XTENSOR_ASSERT(nb <= e.shape(axis));
- svs[axis] = xt::range(e.shape(axis), nb + e.shape(axis));
- xt::strided_view(out, svt) = xt::strided_view(out, svs);
- }
- else if (mode == pad_mode::symmetric)
- {
- XTENSOR_ASSERT(nb <= e.shape(axis));
- svs[axis] = xt::range(2 * nb - 1, nb - 1, -1);
- xt::strided_view(out, svt) = xt::strided_view(out, svs);
- }
- else if (mode == pad_mode::reflect)
- {
- XTENSOR_ASSERT(nb <= e.shape(axis) - 1);
- svs[axis] = xt::range(2 * nb, nb, -1);
- xt::strided_view(out, svt) = xt::strided_view(out, svs);
- }
- else if (mode == pad_mode::edge)
- {
- svs[axis] = xt::range(nb, nb + 1);
- xt::strided_view(out, svt) = xt::broadcast(
- xt::strided_view(out, svs),
- xt::strided_view(out, svt).shape()
- );
- }
- }
- if (ne > static_cast<size_type>(0))
- {
- svt[axis] = xt::range(out.shape(axis) - ne, out.shape(axis));
- if (mode == pad_mode::wrap || mode == pad_mode::periodic)
- {
- XTENSOR_ASSERT(ne <= e.shape(axis));
- svs[axis] = xt::range(nb, nb + ne);
- xt::strided_view(out, svt) = xt::strided_view(out, svs);
- }
- else if (mode == pad_mode::symmetric)
- {
- XTENSOR_ASSERT(ne <= e.shape(axis));
- if (ne == nb + e.shape(axis))
- {
- svs[axis] = xt::range(nb + e.shape(axis) - 1, _, -1);
- }
- else
- {
- svs[axis] = xt::range(nb + e.shape(axis) - 1, nb + e.shape(axis) - ne - 1, -1);
- }
- xt::strided_view(out, svt) = xt::strided_view(out, svs);
- }
- else if (mode == pad_mode::reflect)
- {
- XTENSOR_ASSERT(ne <= e.shape(axis) - 1);
- if (ne == nb + e.shape(axis) - 1)
- {
- svs[axis] = xt::range(nb + e.shape(axis) - 2, _, -1);
- }
- else
- {
- svs[axis] = xt::range(nb + e.shape(axis) - 2, nb + e.shape(axis) - ne - 2, -1);
- }
- xt::strided_view(out, svt) = xt::strided_view(out, svs);
- }
- else if (mode == pad_mode::edge)
- {
- svs[axis] = xt::range(out.shape(axis) - ne - 1, out.shape(axis) - ne);
- xt::strided_view(out, svt) = xt::broadcast(
- xt::strided_view(out, svs),
- xt::strided_view(out, svt).shape()
- );
- }
- }
- svs[axis] = xt::all();
- svt[axis] = xt::all();
- }
- return out;
- }
- /**
- * @brief Pad an array.
- *
- * @param e The array.
- * @param pad_width Number of values padded to the edges of each axis:
- * `{before, after}`.
- * @param mode The type of algorithm to use. [default: `xt::pad_mode::constant`].
- * @param constant_value The value to set the padded values for each axis
- * (used in `xt::pad_mode::constant`).
- * @return The padded array.
- */
- template <class E, class S = typename std::decay_t<E>::size_type, class V = typename std::decay_t<E>::value_type>
- inline auto
- pad(E&& e, const std::vector<S>& pad_width, pad_mode mode = pad_mode::constant, V constant_value = 0)
- {
- std::vector<std::vector<S>> pw(e.shape().size(), pad_width);
- return pad(e, pw, mode, constant_value);
- }
- /**
- * @brief Pad an array.
- *
- * @param e The array.
- * @param pad_width Number of values padded to the edges of each axis.
- * @param mode The type of algorithm to use. [default: `xt::pad_mode::constant`].
- * @param constant_value The value to set the padded values for each axis
- * (used in `xt::pad_mode::constant`).
- * @return The padded array.
- */
- template <class E, class S = typename std::decay_t<E>::size_type, class V = typename std::decay_t<E>::value_type>
- inline auto pad(E&& e, S pad_width, pad_mode mode = pad_mode::constant, V constant_value = 0)
- {
- std::vector<std::vector<S>> pw(e.shape().size(), {pad_width, pad_width});
- return pad(e, pw, mode, constant_value);
- }
- namespace detail
- {
- template <class E, class S>
- inline auto tile(E&& e, const S& reps)
- {
- using size_type = typename std::decay_t<E>::size_type;
- using return_type = temporary_type_t<E>;
- XTENSOR_ASSERT(e.shape().size() == reps.size());
- using new_shape_type = typename return_type::shape_type;
- auto new_shape = xtl::make_sequence<new_shape_type>(e.shape().size());
- xt::xstrided_slice_vector sv(reps.size());
- for (size_type axis = 0; axis < reps.size(); ++axis)
- {
- new_shape[axis] = e.shape(axis) * reps[axis];
- sv[axis] = xt::range(0, e.shape(axis));
- }
- return_type out(new_shape);
- xt::strided_view(out, sv) = e;
- xt::xstrided_slice_vector svs(e.shape().size(), xt::all());
- xt::xstrided_slice_vector svt(e.shape().size(), xt::all());
- for (size_type axis = 0; axis < e.shape().size(); ++axis)
- {
- for (size_type i = 1; i < static_cast<size_type>(reps[axis]); ++i)
- {
- svs[axis] = xt::range(0, e.shape(axis));
- svt[axis] = xt::range(i * e.shape(axis), (i + 1) * e.shape(axis));
- xt::strided_view(out, svt) = xt::strided_view(out, svs);
- svs[axis] = xt::all();
- svt[axis] = xt::all();
- }
- }
- return out;
- }
- }
- /**
- * @brief Tile an array.
- *
- * @param e The array.
- * @param reps The number of repetitions of A along each axis.
- * @return The tiled array.
- */
- template <class E, class S = typename std::decay_t<E>::size_type>
- inline auto tile(E&& e, std::initializer_list<S> reps)
- {
- return detail::tile(std::forward<E>(e), std::vector<S>{reps});
- }
- template <class E, class C, XTL_REQUIRES(xtl::negation<xtl::is_integral<C>>)>
- inline auto tile(E&& e, const C& reps)
- {
- return detail::tile(std::forward<E>(e), reps);
- }
- /**
- * @brief Tile an array.
- *
- * @param e The array.
- * @param reps The number of repetitions of A along the first axis.
- * @return The tiled array.
- */
- template <class E, class S = typename std::decay_t<E>::size_type, XTL_REQUIRES(xtl::is_integral<S>)>
- inline auto tile(E&& e, S reps)
- {
- std::vector<S> tw(e.shape().size(), static_cast<S>(1));
- tw[0] = reps;
- return detail::tile(std::forward<E>(e), tw);
- }
- }
- #endif
|