Commit ecb47cac authored by Michaël Roynard's avatar Michaël Roynard Committed by Edwin Carlinet
Browse files

zip view (PR4)

parent e9730905
......@@ -8,4 +8,6 @@
.vs
.idea
*.pyc
/venv_*/
\ No newline at end of file
compile_commands.json
/venv_*/
/**/.DS_Store
\ No newline at end of file
......@@ -60,6 +60,7 @@
"queue": "cpp",
"stack": "cpp"
},
"python.pythonPath": "/usr/bin/python3",
"C_Cpp.configurationWarnings": "Disabled"
"python.pythonPath": "venv_python37/bin/python",
"C_Cpp.configurationWarnings": "Disabled",
"git.ignoreLimitWarning": true
}
\ No newline at end of file
......@@ -2,6 +2,7 @@
#include <functional>
#include <mln/core/image/view/transform.hpp>
#include <mln/core/image/view/zip.hpp>
#include <mln/core/rangev3/foreach.hpp>
namespace mln::experimental
......@@ -97,14 +98,13 @@ namespace mln::experimental
template <class ICond, class ITrue, class IFalse>
struct where_fn<ICond, ITrue, IFalse, std::enable_if_t<is_a<ITrue, Image>::value && is_a<IFalse, Image>::value>>
{
auto operator()(const ICond& /*cond*/, ITrue /*iftrue*/, IFalse /*iffalse*/) const
auto operator()(const ICond& cond, ITrue iftrue, IFalse iffalse) const
{
auto g = [](bool vcond, auto&& vtrue, auto&& vfalse) -> decltype(auto) {
return (vcond) ? std::forward<decltype(vtrue)>(vtrue) : std::forward<decltype(vfalse)>(vfalse);
auto g = [](auto tuple_ternary_expr) -> decltype(auto) {
return (std::get<0>(tuple_ternary_expr)) ? std::get<1>(tuple_ternary_expr) : std::get<2>(tuple_ternary_expr);
};
// FIXME: to be implemented with imzip
return;
return view::transform(view::zip(cond, iftrue, iffalse), g);
}
};
......@@ -144,42 +144,42 @@ namespace mln::experimental
return view::transform(cond, g);
}
};
} // namespace details
} // namespace details
template <class ICond, class ITrue, class IFalse>
auto where(const Image<ICond>& cond, ITrue iftrue, IFalse iffalse)
{
return details::where_fn<ICond, ITrue, IFalse>()(static_cast<const ICond&>(cond), std::move(iftrue),
std::move(iffalse));
}
template <class ICond, class ITrue, class IFalse>
auto where(const Image<ICond>& cond, ITrue iftrue, IFalse iffalse)
{
return details::where_fn<ICond, ITrue, IFalse>()(static_cast<const ICond&>(cond), std::move(iftrue),
std::move(iffalse));
}
// FIXME: deprecated => replace with algorithm all_of
template <class I>
[[deprecated]] bool all(I ima) {
static_assert(mln::is_a<I, Image>());
static_assert(std::is_convertible<typename I::reference, bool>());
// FIXME: deprecated => replace with algorithm all_of
template <class I>
[[deprecated]] bool all(I ima) {
static_assert(mln::is_a<I, Image>());
static_assert(std::is_convertible<typename I::reference, bool>());
mln_foreach_new (auto&& val, ima.new_values())
if (!val)
return false;
mln_foreach_new (auto&& val, ima.new_values())
if (!val)
return false;
return true;
}
return true;
}
template <class I>
[[deprecated]] bool any(I ima) {
static_assert(mln::is_a<I, Image>());
static_assert(std::is_convertible<typename I::reference, bool>());
template <class I>
[[deprecated]] bool any(I ima) {
static_assert(mln::is_a<I, Image>());
static_assert(std::is_convertible<typename I::reference, bool>());
mln_foreach_new (auto&& val, ima.new_values())
if (val)
return true;
mln_foreach_new (auto&& val, ima.new_values())
if (val)
return true;
return false;
}
return false;
}
#undef MLN_PRIVATE_DEFINE_UNARY_OPERATOR
......
......@@ -208,8 +208,8 @@ namespace mln
return m_ima.index_of_point(p);
}
template <typename dummy = I, typename = std::enable_if_t<(indexable::value && accessible::value)>>
point_type point_at_index(image_index_t<dummy> i) const
template <typename dummy = I>
point_type point_at_index(std::enable_if_t<(indexable::value && accessible::value), image_index_t<dummy>> i) const
{
return m_ima.point_at_index(i);
}
......
......@@ -27,12 +27,6 @@ namespace mln
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 = image_ch_value_t<I, value_type>;
template <typename V>
using ch_value_type = image_ch_value_t<I, V>;
/// \}
......@@ -45,13 +39,15 @@ namespace mln
// 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>>;
/// \}
using concrete_type = image_ch_value_t<I, value_type>;
private:
// FIXME may be a simpler way ?
// note that pixels() can return a proxy convertible to the pixel_type ? Or does it?
// using pixel_range_base_type = decltype(std::declval<I&>.pixels());
// using pixel_proxy_base_type = ::ranges::range_value_t<pixel_proxy_base_type>;
#ifdef PYLENE_CONCEPT_TS_ENABLED
template <concepts::Value V>
#else
template <typename V>
#endif
using ch_value_type = image_ch_value_t<I, V>;
/// \}
public:
/// Pixel type definitions
......@@ -182,7 +178,8 @@ namespace mln
template <class I1, class I2, class F>
class transform2_view : public experimental::Image<transform2_view<I1, I2, F>>
{
using fun_t = F;
using fun_t = F;
using common_category = std::common_type_t<image_category_t<I1>, image_category_t<I2>>;
I1 m_ima1;
I2 m_ima2;
fun_t fun_;
......@@ -191,28 +188,34 @@ namespace mln
/// Type definitions
/// \{
using reference = std::invoke_result_t<F&, typename I1::reference, typename I2::reference>;
using value_type = std::decay_t<reference>;
using point_type = typename I1::point_type;
using domain_type = typename I1::domain_type;
using value_type = std::remove_cv_t<std::remove_reference_t<reference>>;
using point_type = std::common_type_t<image_point_t<I1>, image_point_t<I2>>;
using domain_type = std::common_type_t<image_domain_t<I1>, image_domain_t<I2>>;
/// \}
/// Traits & Image Properties
/// \{
using accessible = std::bool_constant<I1::accessible::value && I2::accessible::value>;
using indexable = std::false_type; // Preservative behavior
using indexable = std::false_type; // Preservative behavior
using view = std::true_type;
using extension_category = mln::extension::none_extension_tag; // Preservative behavior (may be too preservative)
// 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, common_category>, bidirectional_image_tag, common_category>;
using concrete_type = std::common_type_t<image_ch_value_t<I1, value_type>, image_ch_value_t<I2, value_type>>;
using concrete_type = image_ch_value_t<I1, value_type>;
template <class V>
using ch_value_type = image_ch_value_t<I1, V>;
#ifdef PYLENE_CONCEPT_TS_ENABLED
template <concepts::Value V>
#else
template <typename V>
#endif
using ch_value_type = std::common_type_t<image_ch_value_t<I1, V>, image_ch_value_t<I2, V>>;
/// \}
public:
/// Pixel type definitions
/// \{
struct new_pixel_type : pixel_adaptor<image_pixel_t<I1>>, Pixel<new_pixel_type>
struct new_pixel_type : Pixel<new_pixel_type>
{
public:
using point_type = transform2_view::point_type;
......@@ -222,36 +225,27 @@ namespace mln
new_pixel_type(fun_t fun, image_pixel_t<I1> px1, image_pixel_t<I2> px2)
: new_pixel_type::pixel_adaptor{std::move(px1)}
: m_pix1{std::move(px1)}
, m_pix2{std::move(px2)}
, fun_{std::move(fun)}
{
}
new_pixel_type(const new_pixel_type& other)
: new_pixel_type::pixel_adaptor{other}
, m_pix2{other.m_pix2}
, fun_(other.fun_)
{
}
new_pixel_type(new_pixel_type&& other)
: new_pixel_type::pixel_adaptor{std::move(other)}
, m_pix2{std::move(other.m_pix2)}
, fun_(std::move(other.fun_))
{
}
new_pixel_type(const new_pixel_type& other) = default;
new_pixel_type(new_pixel_type&& other) = default;
reference val() const { return std::invoke(fun_, new_pixel_type::pixel_adaptor::val(), m_pix2.val()); }
auto point() const { return new_pixel_type::pixel_adaptor::point(); }
reference val() const { return std::invoke(fun_, m_pix1.val(), m_pix2.val()); }
auto point() const { return m_pix1.point(); }
void advance(point_type p)
{
new_pixel_type::pixel_adaptor::advance(p);
m_pix1.advance(p);
m_pix2.advance(p);
}
private:
fun_t fun_;
image_pixel_t<I1> m_pix1;
image_pixel_t<I2> m_pix2;
fun_t fun_;
};
/// \}
......@@ -262,69 +256,72 @@ namespace mln
{
}
transform2_view(const transform2_view& other)
: m_ima1(other.m_ima1)
, m_ima2(other.m_ima2) /*transform_view::image_adaptor{other}*/
, fun_(other.fun_)
{
}
transform2_view(transform2_view&& other)
: m_ima1(std::move(other.m_ima1))
, m_ima2(std::move(other.m_ima2)) /*transform_view::image_adaptor{std::move(other)}*/
, fun_(std::move(other.fun_))
{
}
transform2_view(const transform2_view& other) = default;
transform2_view(transform2_view&& other) = default;
decltype(auto) concretize() const { return imchvalue<value_type>(this->base()); }
decltype(auto) concretize() const { return m_ima1.template ch_value<value_type>(); };
template <class U>
template <class V>
decltype(auto) ch_value() const
{
return imchvalue<U>(this->base());
return m_ima1.template ch_value<V>();
}
auto domain() const { return m_ima1.domain(); }
auto new_values() { return mln::ranges::view::transform(m_ima1.new_values(), m_ima2.new_values(), fun_); }
auto new_values()
{
static_assert(::ranges::ForwardRange<decltype(m_ima1.new_values())>());
static_assert(::ranges::ForwardRange<decltype(m_ima2.new_values())>());
return mln::ranges::view::transform(m_ima1.new_values(), m_ima2.new_values(), fun_);
}
auto new_pixels()
{
using R1 = decltype(m_ima1.new_pixels());
using R2 = decltype(m_ima2.new_pixels());
using R1 = decltype(m_ima1.new_pixels());
using R2 = decltype(m_ima2.new_pixels());
static_assert(::ranges::ForwardRange<R1>());
static_assert(::ranges::ForwardRange<R2>());
auto pxwrapper = [fun = this->fun_](::ranges::range_reference_t<R1> px1, ::ranges::range_reference_t<R2> px2) {
return new_pixel_type{fun, std::move(px1), std::move(px2)};
};
return mln::ranges::view::transform(m_ima1.new_pixels(), m_ima2.new_pixels(), pxwrapper);
}
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(m_ima1.domain().has(p));
mln_precondition(m_ima2.domain().has(p));
return std::invoke(fun_, m_ima1.at(p), m_ima2.at(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_, m_ima1.at(p), m_ima2.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(m_ima1.domain().has(p));
mln_precondition(m_ima2.domain().has(p));
return {fun_, m_ima1.new_pixel(p), m_ima2.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_, m_ima1.new_pixel_at(p), m_ima2.new_pixel_at(p)};
}
/// \}
};
......@@ -345,6 +342,11 @@ namespace mln
static_assert(mln::is_a<I1, experimental::Image>());
static_assert(mln::is_a<I2, experimental::Image>());
#ifdef PYLENE_CONCEPT_TS_ENABLED
static_assert(mln::concepts::InputImage<I1>);
static_assert(mln::concepts::InputImage<I2>);
#endif
return {std::move(ima1), std::move(ima2), std::move(fun)};
}
} // namespace view
......
#pragma once
#include <mln/core/image/image.hpp>
#include <mln/core/image/view/adaptor.hpp>
#include <mln/core/rangev3/view/zip.hpp>
#include <tuple>
#include <type_traits>
namespace mln
{
template <class... Images>
class zip_view : public experimental::Image<zip_view<Images...>>
{
using tuple_t = std::tuple<Images...>;
using I0 = typename std::tuple_element<0, tuple_t>::type;
using common_category = std::common_type_t<image_category_t<Images>...>;
tuple_t m_images;
public:
/// Type definitions
/// \{
using reference = std::tuple<image_reference_t<Images>...>;
using value_type = std::tuple<image_value_t<Images>...>;
using point_type = std::common_type_t<image_point_t<Images>...>;
using domain_type = std::common_type_t<image_domain_t<Images>...>;
/// \}
/// Traits & Image Properties
/// \{
using accessible = std::conjunction<typename Images::accessible...>;
using indexable = std::false_type; // Preservative behavior
using view = std::true_type;
using extension_category = mln::extension::none_extension_tag; // Preservative behavior
// Zip 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, common_category>, bidirectional_image_tag, common_category>;
using concrete_type = image_ch_value_t<I0, typename I0::value_type>;
#ifdef PYLENE_CONCEPT_TS_ENABLED
template <concepts::Value V>
#else
template <typename V>
#endif
using ch_value_type = image_ch_value_t<I0, V>;
/// \}
static_assert(std::conjunction_v<std::is_same<image_point_t<Images>, point_type>...>,
"The point type of all images to be zipped must be the same.");
public:
/// Pixel type definitions
/// \{
struct new_pixel_type : Pixel<new_pixel_type>
{
using reference = zip_view::reference;
using value_type = zip_view::value_type;
using site_type[[deprecated]] = zip_view::point_type;
using point_type = zip_view::point_type;
new_pixel_type(image_pixel_t<Images>... pixels)
: m_pixels{std::move(pixels)...}
{
}
new_pixel_type(const new_pixel_type&) = default;
new_pixel_type(new_pixel_type&&) = default;
new_pixel_type& operator=(const new_pixel_type&) = delete;
new_pixel_type& operator=(new_pixel_type&&) = delete;
point_type point() const { return std::get<0>(m_pixels).point(); }
reference val() const
{
auto build_value = [](auto&&... pixels) -> reference { return {pixels.val()...}; };
return std::apply(build_value, m_pixels);
}
void advance(point_type q)
{
auto g = [q](auto&&... pixels) { (pixels.advance(q), ...); };
std::apply(g, m_pixels);
}
private:
std::tuple<image_pixel_t<Images>...> m_pixels;
};
/// \}
zip_view(Images... images)
: m_images{std::move(images)...}
{
}
zip_view(const zip_view<Images...>&) = default;
zip_view(zip_view<Images...>&&) = default;
zip_view<Images...>& operator=(const zip_view<Images...>&) = delete;
zip_view<Images...>& operator=(zip_view<Images...>&&) = delete;
auto domain() const { return std::get<0>(m_images).domain(); }
decltype(auto) concretize() const { return std::get<0>(m_images).concretize(); }
#ifdef PYLENE_CONCEPT_TS_ENABLED
template <concepts::Value V>
#else
template <typename V>
#endif
decltype(auto) ch_value() const
{
return std::get<0>(m_images).template ch_value<V>();
}
auto new_values()
{
auto g_new_values = [](auto&&... images) { return ranges::view::zip(images.new_values()...); };
return std::apply(g_new_values, m_images);
}
auto new_pixels()
{
auto g_new_pixels = [](auto&&... images) {
return ranges::view::zip_with([](auto&&... pixels) { return new_pixel_type(pixels...); },
images.new_pixels()...);
};
return std::apply(g_new_pixels, m_images);
}
/// Accessible-image related methods
/// \{
template <typename Ret = reference>
std::enable_if_t<accessible::value, Ret> operator()(point_type p)
{
mln_precondition(all_domain_has(p));
return this->at(p);
}
template <typename Ret = reference>
std::enable_if_t<accessible::value, Ret> at(point_type p)
{
auto g = [p](auto&&... images) { return std::forward_as_tuple(images.at(p)...); };
return std::apply(g, m_images);
}
template <typename Ret = new_pixel_type>
std::enable_if_t<accessible::value, Ret> new_pixel(point_type p)
{
mln_precondition(all_domain_has(p));
return this->new_pixel_at(p);
}
template <typename Ret = new_pixel_type>
std::enable_if_t<accessible::value, Ret> new_pixel_at(point_type p)
{
auto g = [p](auto&&... images) { return new_pixel_type(images.new_pixel_at(p)...); };
return std::apply(g, m_images);
}
/// \}
private:
template <typename dummy = bool>
std::enable_if_t<accessible::value, dummy> all_domain_has(point_type p)
{
auto g_has = [p](auto&&... images) { return (images.domain().has(p) && ...); };
return std::apply(g_has, m_images);
}
};
namespace view
{
template <class... Images>
zip_view<Images...> zip(Images... images)
{
static_assert(std::conjunction_v<is_a<Images, experimental::Image>...>, "All zip arguments must be images.");
return zip_view<Images...>(std::move(images)...);
}
} // namespace view
} // namespace mln
......@@ -33,13 +33,21 @@ namespace mln::ranges
{
private:
// Very bad way to access the private member
auto fun() const { return reinterpret_cast<const detail::iter_transform_view_public<Rng, Fun>*>(this)->fun_; }
auto cfun() const { return reinterpret_cast<const detail::iter_transform_view_public<Rng, Fun>*>(this)->fun_; }
auto fun() { return reinterpret_cast<detail::iter_transform_view_public<Rng, Fun>*>(this)->fun_; }
public:
using ::ranges::iter_transform_view<Rng, Fun>::iter_transform_view;
template <typename U = void, typename = std::enable_if_t<is_segmented_range_v<Rng>, U>>
auto rows() const
{
return ::ranges::view::transform(this->base().rows(),
[fun_ = cfun()](auto row) { return ::ranges::view::iter_transform(row, fun_); });
}
template <typename U = void, typename = std::enable_if_t<is_segmented_range_v<Rng>, U>>
auto rows()