Commit f329e4b1 authored by Michaël Roynard's avatar Michaël Roynard
Browse files

Fix adaptor/transform

parent 9c2000d3
Pipeline #12648 failed with stages
in 7 minutes and 42 seconds
......@@ -7,15 +7,15 @@ Include :file:`<mln/labeling/local_extrema.hpp>`
#. .. cpp:function:: \
template <class Label_t, class I, class N> \
ch_value_t<I, Label_t> local_minima(const Image<I>& input, const Neighborhood<N>& nbh, int& nlabel)
image_ch_value_t<I, Label_t> local_minima(const Image<I>& input, const Neighborhood<N>& nbh, int& nlabel)
#. .. cpp:function:: \
template <class Label_t, class I, class N> \
ch_value_t<I, Label_t> local_maxima(const Image<I>& input, const Neighborhood<N>& nbh, int& nlabel)
image_ch_value_t<I, Label_t> local_maxima(const Image<I>& input, const Neighborhood<N>& nbh, int& nlabel)
#. .. cpp:function:: \
template <class Label_t, class I, class N, class Compare> \
ch_value_t<I, Label_t> local_minima(const Image<I>& input, const Neighborhood<N>& nbh, int& nlabel, Compare cmp)
image_ch_value_t<I, Label_t> local_minima(const Image<I>& input, const Neighborhood<N>& nbh, int& nlabel, Compare cmp)
Labelize the local minima (1) or maxima (2) of an image with an optional
comparaison function ``cmp`` (3).
......
......@@ -7,7 +7,7 @@ Include :file:`<mln/morpho/watershed.hpp>`
.. cpp:function:: template <class Label_t, class I, class N> \
ch_value_t<I, Label_t> watershed(const Image<I>& ima, const Neighborhood<N>& nbh, int& nlabel)
image_ch_value_t<I, Label_t> watershed(const Image<I>& ima, const Neighborhood<N>& nbh, int& nlabel)
Watershed by immersion as defined in [BM92]_. The catchment basins are
labeled from 1 to n, and the special label 0 is used for watershed lines.
......
......@@ -23,18 +23,27 @@ namespace mln
{
typedef I type;
};
}
} // namespace internal
struct init
{
};
template <typename I>
struct New_Image;
template <typename V, class I>
internal::initializer<mln_ch_value(I, V), I> imchvalue(const Image<I>& ref);
template <typename V, class I>
internal::initializer<mln_ch_value(I, V), I> imchvalue_new(const New_Image<I>& ref);
template <class I>
internal::initializer<mln_concrete(I), I> imconcretize(const Image<I>& ref);
template <class I>
internal::initializer<mln_concrete(I), I> imconcretize_new(const New_Image<I>& ref);
/******************************************/
/**** Implementation ****/
/******************************************/
......@@ -216,7 +225,7 @@ namespace mln
int m_border;
};
} // end of namespace mln::internal
} // namespace internal
template <typename V, class I>
internal::initializer<mln_ch_value(I, V), I> imchvalue(const Image<I>& _ref)
......@@ -225,6 +234,13 @@ namespace mln
return internal::initializer<mln_ch_value(I, V), I>(ref);
}
template <typename V, class I>
internal::initializer<mln_ch_value(I, V), I> imchvalue_new(const New_Image<I>& _ref)
{
const I& ref = *static_cast<const I*>(&_ref);
return internal::initializer<mln_ch_value(I, V), I>(ref);
}
template <class I>
internal::initializer<mln_concrete(I), I> imconcretize(const Image<I>& _ref)
{
......@@ -232,6 +248,13 @@ namespace mln
return internal::initializer<mln_concrete(I), I>(ref);
}
template <class I>
internal::initializer<mln_concrete(I), I> imconcretize_new(const New_Image<I>& _ref)
{
const I& ref = *static_cast<const I*>(&_ref);
return internal::initializer<mln_concrete(I), I>(ref);
}
} // end of namespace mln
#endif //! MLN_CORE_IMAGE_INTERNAL_INITIALIZER_HPP
#pragma once
// FIXME: backward compatibility
#include <mln/core/image_traits.hpp> // image_ch_value_t
namespace mln
{
......@@ -47,8 +50,9 @@ namespace mln
// Change value type trait
template <class I, class V>
using image_ch_value_t = typename I::template ch_value_type<V>;
// FIXME: defined in mln/core/image_traits
// template <class I, class V>
// using image_ch_value_t = typename I::template ch_value_type<V>;
// Category trait
template <class I>
......
......@@ -101,7 +101,9 @@ namespace mln
template <class I>
struct image_adaptor : detail::image_adaptor_base_indexable<I>, detail::image_adaptor_base_with_extension<I>
struct image_adaptor : detail::image_adaptor_base_indexable<I>,
detail::image_adaptor_base_with_extension<I>,
New_Image<I>
{
public:
/// Type definitions
......
......@@ -3,6 +3,7 @@
#include <mln/core/image/image.hpp>
#include <mln/core/image/view/adaptor.hpp>
#include <mln/core/rangev3/view/transform.hpp>
#include <type_traits>
namespace mln
......@@ -10,7 +11,7 @@ namespace mln
template <class I, class F>
class transform_view : public image_adaptor<I>, public New_Image<transform_view<I, F>>
class transform_view : public image_adaptor<I>
{
using fun_t = F;
fun_t fun_;
......@@ -18,27 +19,32 @@ namespace mln
public:
/// Type definitions
/// \{
using reference = std::invoke_result_t<F&, typename I::reference>;
using value_type = std::decay_t<reference>;
using point_type = typename I::point_type;
using reference = std::invoke_result_t<F&, image_reference_t<I>>;
using value_type = std::remove_cv_t<std::remove_reference_t<reference>>;
using point_type = image_point_t<I>;
using domain_type = image_domain_t<I>;
static_assert(!(std::is_rvalue_reference_v<reference> && !std::is_reference_v<typename I::reference>),
static_assert(!(std::is_rvalue_reference_v<reference> && !std::is_reference_v<image_reference_t<I>>),
"The transformed image returns a temporary and the mapping function is a projection.\n"
"This is building a dangling reference.");
using concrete_type = ch_value_t<I, value_type>;
using concrete_type = image_ch_value_t<I, value_type>;
template <class V>
using ch_value_type = ch_value_t<I, V>;
template <typename V>
using ch_value_type = image_ch_value_t<I, V>;
/// \}
/// Traits & Image Properties
/// \{
using accessible = typename I::accessible;
using indexable = typename I::indexable;
using extension_category = mln::extension::none_extension_tag; // FIXME: should be improved
using accessible = image_accessible_t<I>;
using indexable = image_indexable_t<I>;
using view = std::true_type;
using extension_category = image_extension_category_t<I>;
// Transform doesn't preserve contiguity, so it decays from raw_image_tag
using category_type = std::conditional_t<std::is_base_of_v<raw_image_tag, image_category_t<I>>,
bidirectional_image_tag, image_category_t<I>>;
/// \}
private:
......@@ -82,6 +88,10 @@ namespace mln
/// \}
/////////////////
// Image proxy //
/////////////////
transform_view(I ima, F fun)
: transform_view::image_adaptor{std::move(ima)}
, fun_{std::move(fun)}
......@@ -99,61 +109,80 @@ namespace mln
{
}
auto new_values() { return mln::ranges::view::transform(this->base().new_values(), fun_); }
decltype(auto) concretize() const { return imchvalue<value_type>(this->base()); }
auto new_pixels()
{
using R = decltype(this->base().new_pixels());
auto pxwrapper = [fun = this->fun_](::ranges::range_value_t<R> px) { return new_pixel_type{fun, std::move(px)}; };
return mln::ranges::view::transform(this->base().new_pixels(), pxwrapper);
}
template <class U>
decltype(auto) concretize() const { return imchvalue_new<value_type>(this->base()); }
#ifdef PYLENE_CONCEPT_TS_ENABLED
template <concepts::Value Val>
#else
template <typename Val>
#endif
decltype(auto) ch_value() const
{
return imchvalue<U>(this->base());
return imchvalue_new<Val>(this->base());
}
auto new_values() { return mln::ranges::view::transform(this->base().new_values(), fun_); }
auto new_pixels()
/// Indexable-image related methods
/// \{
template <typename dummy = I>
reference operator[](image_index_t<dummy> i)
{
using R = decltype(this->base().new_pixels());
auto pxwrapper = [fun = this->fun_](::ranges::range_value_t<R> px) { return new_pixel_type{fun, std::move(px)}; };
return mln::ranges::view::transform(this->base().new_pixels(), pxwrapper);
return std::invoke(fun_, this->base()[i]);
}
/// \}
template <typename dummy = reference>
std::enable_if_t<accessible::value, dummy> operator()(point_type p)
/// Accessible-image related methods
/// \{
template <typename Ret = reference>
std::enable_if_t<accessible::value, Ret> operator()(point_type p)
{
mln_precondition(this->base().domain().has(p));
mln_precondition(this->domain().has(p));
return std::invoke(fun_, this->base()(p));
}
template <typename dummy = reference>
std::enable_if_t<accessible::value, dummy> at(point_type p)
template <typename Ret = reference>
std::enable_if_t<accessible::value, Ret> at(point_type p)
{
return std::invoke(fun_, this->base().at(p));
}
template <typename dummy = new_pixel_type>
std::enable_if_t<accessible::value, dummy> new_pixel(point_type p)
template <typename Ret = new_pixel_type>
std::enable_if_t<accessible::value, Ret> new_pixel(point_type p)
{
mln_precondition(this->base().domain().has(p));
mln_precondition(this->domain().has(p));
return {fun_, this->base().new_pixel(p)};
}
template <typename dummy = new_pixel_type>
std::enable_if_t<accessible::value, dummy> new_pixel_at(point_type p)
template <typename Ret = new_pixel_type>
std::enable_if_t<accessible::value, Ret> new_pixel_at(point_type p)
{
return {fun_, this->base().new_pixel_at(p)};
}
/// \}
template <typename dummy = I>
reference operator[](typename dummy::size_type i)
{
return std::invoke(fun_, this->base()[i]);
}
/// Raw-image related methods
/// \{
auto data() const = delete;
auto data() = delete;
auto strides(int) const = delete;
/// \}
};
template <class I1, class I2, class F>
class transform2_view : public New_Image<transform2_view<I1, I2, F>>
{
using fun_t = F; // FIXME something with semiregular_t<F> ?
using fun_t = F;
I1 m_ima1;
I2 m_ima2;
fun_t fun_;
......@@ -174,10 +203,10 @@ namespace mln
using indexable = std::false_type; // Preservative behavior
using extension_category = mln::extension::none_extension_tag; // Preservative behavior (may be too preservative)
using concrete_type = ch_value_t<I1, value_type>;
using concrete_type = image_ch_value_t<I1, value_type>;
template <class V>
using ch_value_type = ch_value_t<I1, V>;
using ch_value_type = image_ch_value_t<I1, V>;
/// \}
public:
......
......@@ -23,7 +23,7 @@
namespace mln
{
template <class I, class V>
using ch_value_t = typename I::template ch_value_type<V>;
using image_ch_value_t = typename I::template ch_value_type<V>;
struct [[deprecated]] image_dynamic_tag
{
......@@ -37,7 +37,7 @@ namespace mln
struct Image;
template <typename I>
struct image_traits : image_traits<typename std::decay<I>::type>
struct image_traits : image_traits<typename std::decay<I>::type>
{
};
......@@ -217,7 +217,7 @@ namespace mln
};
#define MLN_GENERATE_META_IMAGE_OPERATORS(meta_op, op) \
struct meta_op \
struct meta_op \
{ \
template <typename Image> \
struct apply \
......@@ -244,6 +244,6 @@ namespace mln
MLN_GENERATE_META_IMAGE_OPERATORS(image_meta_const_value_iterator, image_const_value_iterator)
MLN_GENERATE_META_IMAGE_OPERATORS(image_meta_pixel_iterator, image_pixel_iterator)
MLN_GENERATE_META_IMAGE_OPERATORS(image_meta_const_pixel_iterator, image_const_pixel_iterator)
}
} // namespace mln
#endif /* !MLN_CORE_IMAGE_TRAITS_HPP */
......@@ -10,6 +10,8 @@
#include <mln/core/rangev3/foreach.hpp>
#include <mln/core/rangev3/view/zip.hpp>
#include <helpers.hpp>
#include <gtest/gtest.h>
......@@ -18,14 +20,10 @@ namespace archetypes = mln::archetypes;
#ifdef PYLENE_CONCEPT_TS_ENABLED
template <concepts::Image Ima>
template <concepts::ViewImage Ima>
void foo(Ima)
{
}
template <concepts::Pixel Pix>
void bar(Pix)
{
}
#endif // PYLENE_CONCEPT_TS_ENABLED
TEST(Core, TransformedImage_transform_byval_rvalue)
......@@ -38,30 +36,39 @@ TEST(Core, TransformedImage_transform_byval_rvalue)
iota(ima, 0);
{
auto out = view::transform(ima, [](int x) { return x * 2; });
#ifdef PYLENE_CONCEPT_TS_ENABLED
// bar(std::decay_t<decltype(out)>::new_pixel_type{});
foo(out);
#endif // PYLENE_CONCEPT_TS_ENABLED
#ifdef PYLENE_CONCEPT_TS_ENABLED
static_assert(concepts::ConcreteImage<decltype(ima)>);
static_assert(concepts::OutputImage<decltype(ima)>);
static_assert(concepts::ViewImage<decltype(out)>);
static_assert(concepts::IndexableAndAccessibleImage<decltype(out)>);
static_assert(concepts::BidirectionalImage<decltype(out)>);
static_assert(not concepts::RawImage<decltype(out)>);
static_assert(not concepts::OutputImage<decltype(out)>);
#endif // PYLENE_CONCEPT_TS_ENABLED
// Test pixel iteration
// check that properties of pixels are preserved (point + index)
{mln_foreach_new ((auto[px1, px2]),
mln::ranges::view::zip(ima.new_pixels(), out.new_pixels())){ASSERT_EQ(px1.point(), px2.point());
// ASSERT_EQ(px1.index(), px2.index());
ASSERT_EQ(2 * px1.val(), px2.val());
// ASSERT_EQ(&px2->image(), &out);
{
auto rng = mln::ranges::view::zip(ima.new_pixels(), out.new_pixels());
mln_foreach_new ((auto [px1, px2]), rng)
{
ASSERT_EQ(px1.point(), px2.point());
// ASSERT_EQ(px1.index(), px2.index());
ASSERT_EQ(2 * px1.val(), px2.val());
// ASSERT_EQ(&px2->image(), &out);
}
}
// Test value iteration
{
auto rng = mln::ranges::view::zip(ima.new_values(), out.new_values());
mln_foreach_new ((auto [v1, v2]), rng)
ASSERT_EQ(2 * v1, v2);
}
}
}
// Test value iteration
{
mln_foreach_new ((auto[v1, v2]), mln::ranges::view::zip(ima.new_values(), out.new_values()))
ASSERT_EQ(2 * v1, v2);
}
}
}
TEST(Core, TransformedImage_transform_byval_chain)
{
using namespace mln;
......@@ -73,24 +80,36 @@ TEST(Core, TransformedImage_transform_byval_chain)
{
auto out = view::transform(view::transform(ima, [](int x) { return x * 2; }), [](int x) { return x * 2; });
#ifdef PYLENE_CONCEPT_TS_ENABLED
static_assert(concepts::ConcreteImage<decltype(ima)>);
static_assert(concepts::OutputImage<decltype(ima)>);
static_assert(concepts::ViewImage<decltype(out)>);
static_assert(concepts::IndexableAndAccessibleImage<decltype(out)>);
static_assert(concepts::BidirectionalImage<decltype(out)>);
static_assert(not concepts::RawImage<decltype(out)>);
static_assert(not concepts::OutputImage<decltype(out)>);
#endif // PYLENE_CONCEPT_TS_ENABLED
// Test pixel iteration
// check that properties of pixels are preserved (point + index)
{mln_foreach_new ((auto[px1, px2]),
mln::ranges::view::zip(ima.new_pixels(), out.new_pixels())){ASSERT_EQ(px1.point(), px2.point());
// ASSERT_EQ(px1.index(), px2.index());
ASSERT_EQ(4 * px1.val(), px2.val());
// ASSERT_EQ(&px2->image(), &out);
{
auto rng = mln::ranges::view::zip(ima.new_pixels(), out.new_pixels());
mln_foreach_new ((auto [px1, px2]), rng)
{
ASSERT_EQ(px1.point(), px2.point());
// ASSERT_EQ(px1.index(), px2.index());
ASSERT_EQ(4 * px1.val(), px2.val());
// ASSERT_EQ(&px2->image(), &out);
}
}
// Test value iteration
{
mln_foreach_new ((auto [v1, v2]), mln::ranges::view::zip(ima.new_values(), out.new_values()))
ASSERT_EQ(4 * v1, v2);
}
}
}
// Test value iteration
{
mln_foreach_new ((auto[v1, v2]), mln::ranges::view::zip(ima.new_values(), out.new_values()))
ASSERT_EQ(4 * v1, v2);
}
}
}
TEST(Core, Transform_Support_Writable)
{
using namespace mln;
......@@ -104,6 +123,23 @@ TEST(Core, Transform_Support_Writable)
auto c2 = view::transform(ima, [](const std::pair<int, int>& x) { return x.second; });
fill(ima, std::make_pair(12, 12));
#ifdef PYLENE_CONCEPT_TS_ENABLED
static_assert(concepts::ConcreteImage<decltype(ima)>);
static_assert(concepts::OutputImage<decltype(ima)>);
static_assert(concepts::ViewImage<decltype(c1)>);
static_assert(concepts::IndexableAndAccessibleImage<decltype(c1)>);
static_assert(concepts::BidirectionalImage<decltype(c1)>);
static_assert(not concepts::RawImage<decltype(c1)>);
static_assert(concepts::OutputImage<decltype(c1)>);
static_assert(concepts::ViewImage<decltype(c2)>);
static_assert(concepts::IndexableAndAccessibleImage<decltype(c2)>);
static_assert(concepts::BidirectionalImage<decltype(c2)>);
static_assert(not concepts::RawImage<decltype(c2)>);
static_assert(not concepts::OutputImage<decltype(c2)>);
#endif // PYLENE_CONCEPT_TS_ENABLED
// FIXME: use fill once adapted
mln_foreach_new (auto&& val, c1.new_values())
val = 69;
......@@ -113,29 +149,32 @@ TEST(Core, Transform_Support_Writable)
// Test pixel iteration
// check that properties of pixels are preserved (point + index)
{mln_foreach_new ((auto[px1, px2, px3]),
mln::ranges::view::zip(ima.new_pixels(), c1.new_pixels(), c2.new_pixels())){
{
auto rng = mln::ranges::view::zip(ima.new_pixels(), c1.new_pixels(), c2.new_pixels());
mln_foreach_new ((auto [px1, px2, px3]), rng)
{
ASSERT_EQ(px1.point(), px2.point());
ASSERT_EQ(px1.point(), px3.point());
// ASSERT_EQ(px1.index(), px2.index());
ASSERT_EQ(std::make_pair(69, 12), px1.val());
ASSERT_EQ(69, px2.val());
ASSERT_EQ(12, px3.val());
// ASSERT_EQ(&px2->image(), &out);
}
}
// Test value iteration
{
mln_foreach_new ((auto[v1, v2, v3]), mln::ranges::view::zip(ima.new_values(), c1.new_values(), c2.new_values()))
{
ASSERT_EQ(std::make_pair(69, 12), v1);
ASSERT_EQ(69, v2);
ASSERT_EQ(12, v3);
ASSERT_EQ(px1.point(), px3.point());
// ASSERT_EQ(px1.index(), px2.index());
ASSERT_EQ(std::make_pair(69, 12), px1.val());
ASSERT_EQ(69, px2.val());
ASSERT_EQ(12, px3.val());
// ASSERT_EQ(&px2->image(), &out);
}
}
// Test value iteration
{
auto rng = mln::ranges::view::zip(ima.new_values(), c1.new_values(), c2.new_values());
mln_foreach_new ((auto [v1, v2, v3]), rng)
{
ASSERT_EQ(std::make_pair(69, 12), v1);
ASSERT_EQ(69, v2);
ASSERT_EQ(12, v3);
}
}
}
}
}
}
int times_two(int x)
{
......@@ -151,6 +190,16 @@ TEST(Core, Transform_Supports_Function)
auto c = mln::view::transform(ima, times_two);
#ifdef PYLENE_CONCEPT_TS_ENABLED
static_assert(concepts::ConcreteImage<decltype(ima)>);
static_assert(concepts::OutputImage<decltype(ima)>);
static_assert(concepts::ViewImage<decltype(c)>);
static_assert(concepts::IndexableAndAccessibleImage<decltype(c)>);
static_assert(concepts::BidirectionalImage<decltype(c)>);
static_assert(not concepts::RawImage<decltype(c)>);
static_assert(not concepts::OutputImage<decltype(c)>);
#endif // PYLENE_CONCEPT_TS_ENABLED
// FIXME: ASSERT_IMAGES_EQ(ref, c);
}
......@@ -164,11 +213,19 @@ TEST(Core, Transform_Supports_PointerToMemberFunction)
auto c = mln::view::transform(ima, &std::pair<int, int>::first);
// FIXME: mln::fill(c, 69);
#ifdef PYLENE_CONCEPT_TS_ENABLED
static_assert(concepts::ConcreteImage<decltype(ima)>);
static_assert(concepts::OutputImage<decltype(ima)>);
static_assert(concepts::ViewImage<decltype(c)>);
static_assert(concepts::IndexableAndAccessibleImage<decltype(c)>);
static_assert(concepts::BidirectionalImage<decltype(c)>);
static_assert(not concepts::RawImage<decltype(c)>);
static_assert(concepts::OutputImage<decltype(c)>);
#endif // PYLENE_CONCEPT_TS_ENABLED
// FIXME: mln::fill(c, 69);
mln_foreach_new (auto& val, c.new_values())
val = 69;
// FIXME: ASSERT_IMAGES_EQ(ref, c);
}