Commit 7fe0be81 authored by Edwin Carlinet's avatar Edwin Carlinet

Implement dispatch by SE decomposition in dilation canvas.

parent 9dd47e5b
......@@ -4,9 +4,12 @@
#include <mln/core/extension/extension.hpp>
#include <mln/core/extension/fill.hpp>
#include <mln/core/image/image.hpp>
#include <mln/core/algorithm/copy.hpp>
#include <mln/kernelv2/kernel.hpp>
#include <mln/morpho/se/se.hpp>
#include <mln/morpho/canvas/private/dilation_by_periodic_line.hpp>
namespace mln
{
......@@ -19,15 +22,14 @@ namespace mln
//
// struct dilation_like_operations_traits
// {
// typedef ... support_incremental;
// typedef ... aggregate_type;
// typedef ... incremental_aggregate_type;
// A0 incremental_aggregate;
// A1 aggregate;
// ... zero() constexpr;
// T sup(T x, T, y) const;
// };
template <class I, class SE, class Compare, class J, class OpTraits>
void dilation_like(const Image<I>& ima, const StructuringElement<SE>& nbh, Compare cmp, Image<J>& output,
OpTraits __op__);
template <class I, class SE, class J, class OpTraits>
void dilation_like(const Image<I>& ima, const StructuringElement<SE>& nbh, Image<J>& output, OpTraits __op__);
/******************************************/
/**** Implementation ****/
......@@ -41,16 +43,15 @@ namespace mln
/// This specialization is used when:
/// * The SE is incremental
/// * The feature has the `untake` method
template <class I, class SE, class Compare, class J, class OpTraits>
void dilate_like_1(const I& ima, const SE& nbh, Compare cmp, J& out, OpTraits __op__,
template <class I, class SE, class J, class OpTraits>
void dilate_like_1(const I& ima, const SE& nbh, J& out, OpTraits __op__,
std::true_type __is_incremental__)
{
namespace ker = mln::kernel;
(void)__is_incremental__;
(void)__op__;
(void)cmp; // FIXME: should not be ignored
ker::Aggregate<typename OpTraits::incremental_aggregate_type> A;
auto hsup = __op__.accu_hsup;
ker::Aggregate<decltype(hsup)> A(hsup);
ker::Point p;
ker::Neighbor n;
auto f = ker::make_image_expr<0>(ima);
......@@ -62,15 +63,14 @@ namespace mln
/// \brief Basic implementation
/// This specialization is used when:
/// * Either the SE is not incremental or nor the feature has the `untake` method.
template <class I, class SE, class Compare, class J, class OpTraits>
void dilate_like_1(const I& ima, const SE& nbh, Compare cmp, J& out, OpTraits __op__,
std::false_type __is_incremental__)
template <class I, class SE, class J, class OpTraits>
void dilate_like_1(const I& ima, const SE& nbh, J& out, OpTraits __op__, std::false_type __is_incremental__)
{
namespace ker = mln::kernel;
(void)__is_incremental__;
(void)__op__;
ker::Aggregate<typename OpTraits::aggregate_type> A(cmp);
auto sup = __op__.accu_sup;
ker::Aggregate<decltype(sup)> A(sup);
ker::Point p;
ker::Neighbor n;
auto f = ker::make_image_expr<0>(ima);
......@@ -80,9 +80,8 @@ namespace mln
ker::execute(expr, nbh);
}
template <class I, class SE, class Compare, class J, class OpTraits>
void dilate_like_0(const I& ima, const SE& nbh, Compare cmp, J& out, OpTraits __op__,
std::true_type __has_extension__)
template <class I, class SE, class J, class OpTraits>
void dilate_like_0(const I& ima, const SE& nbh, J& out, OpTraits __op__, std::true_type __has_extension__)
{
(void)__has_extension__;
......@@ -105,18 +104,17 @@ namespace mln
if (extension::need_adjust(ima, nbh))
{
mln::trace::warn("Slow version because input image extension is not wide enough.");
dilate_like_1(extension::add_value_extension(ima, v), nbh, cmp, out, __op__, is_incremental());
dilate_like_1(extension::add_value_extension(ima, v), nbh, out, __op__, is_incremental());
}
else
{
extension::fill(ima, v);
dilate_like_1(ima, nbh, cmp, out, __op__, is_incremental());
dilate_like_1(ima, nbh, out, __op__, is_incremental());
}
}
template <class I, class SE, class Compare, class J, class OpTraits>
void dilate_like_0(const I& ima, const SE& nbh, Compare cmp, J& out, OpTraits __op__,
std::false_type __has_extension__)
template <class I, class SE, class J, class OpTraits>
void dilate_like_0(const I& ima, const SE& nbh, J& out, OpTraits __op__, std::false_type __has_extension__)
{
(void)__has_extension__;
mln::trace::warn("Slow version because input image has no extension.");
......@@ -136,20 +134,83 @@ namespace mln
mln::trace::warn("Slow because the image has no New Line Support");
mln_value(I) v = __op__.zero();
dilate_like_1(extension::add_value_extension(ima, v), nbh, cmp, out, __op__, is_incremental());
dilate_like_1(extension::add_value_extension(ima, v), nbh, out, __op__, is_incremental());
}
} // end of namespace mln::morpho::canvas::impl
namespace overload
{
// Generic implementation
template <class I, class SE, class Compare, class J, class OpTraits>
void dilation_like(const Image<I>& ima, const StructuringElement<SE>& nbh, Compare cmp, Image<J>& output,
OpTraits __op__)
// 2. Try a specific version of the algorithm.
// This is the generic verion
template <class I, class SE, class J, class OpTraits, class Domain>
void dilation_dispatch_2(const I& input, const SE& se, J& output, OpTraits __op__, Domain __domain__)
{
(void) __domain__;
impl::dilate_like_0(input, se, output, __op__, typename image_has_extension<I>::type());
}
// This is the version for regular 2D domain with periodic_line
template <class I, class Compare, class J, class OpTraits>
void dilation_dispatch_2(const I& input, const se::periodic_line2d& line, J& output, OpTraits __op__, box2d __domain__)
{
(void) __domain__;
(void) input;
mln::copy(input, output);
auto sup = [__op__](auto x, auto y) { return __op__.sup(x,y); };
mln::morpho::internal::dilation_by_periodic_line(output, line, sup, __op__.zero());
}
// 2.bis Inplace version
// Inplace generic version
template <class I, class SE, class OpTraits, class Domain>
void dilation_dispatch_2_inplace(I& f, const SE& se, OpTraits __op__, Domain __domain__)
{
(void) __domain__;
// Fixme how to avoid copy and just swap images between iterations ?
mln_concrete(I) temporary = imconcretize(f);
impl::dilate_like_0(f, se, temporary, __op__, typename image_has_extension<I>::type());
mln::copy(temporary, f);
}
// Inplace version for 2D regular domain with periodic line
template <class I, class OpTraits>
void dilation_dispatch_2_inplace(I& f, const se::periodic_line2d& line, OpTraits __op__, box2d __domain__)
{
impl::dilate_like_0(exact(ima), exact(nbh), cmp, exact(output), __op__,
typename image_has_extension<I>::type());
(void) __domain__;
auto sup = [__op__](auto x, auto y) { return __op__.sup(x,y); };
mln::morpho::internal::dilation_by_periodic_line(f, line, sup, __op__.zero());
}
// 1. Try decomposing in smaller SE
template <class I, class SE, class J, class OpTraits>
void dilation_dispatch_1(const I& ima, const SE& nbh, J& output, OpTraits __op__,
std::false_type __is_decomposable)
{
mln_entering("mln::morpho::canvas::dilation (not-decomposed)")
(void) __is_decomposable;
dilation_dispatch_2(ima, nbh, output, __op__, ima.domain());
}
template <class I, class SE, class J, class OpTraits>
void dilation_dispatch_1(const I& input, const SE& nbh, J& output, OpTraits __op__,
std::true_type __is_decomposable)
{
(void) __is_decomposable;
if (nbh.decomposable())
{
mln_entering("mln::morpho::canvas::dilation (decomposed)")
auto ses = nbh.decompose();
mln::copy(input, output);
for (auto&& se : ses)
dilation_dispatch_2_inplace(output, se, __op__, output.domain());
}
else
{
mln_entering("mln::morpho::canvas::dilation (not-decomposed)")
dilation_dispatch_2(input, nbh, output, __op__, output.domain());
}
}
}
......@@ -159,8 +220,6 @@ namespace mln
} // end of namespace mln
#include <mln/morpho/canvas/dilation_like.spe.hpp>
namespace mln
{
......@@ -170,11 +229,10 @@ namespace mln
namespace canvas
{
template <class I, class SE, class Compare, class J, class OpTraits>
void dilation_like(const Image<I>& ima, const StructuringElement<SE>& nbh, Compare cmp, Image<J>& output,
OpTraits __op__)
template <class I, class SE, class J, class OpTraits>
void dilation_like(const Image<I>& ima, const StructuringElement<SE>& nbh, Image<J>& output, OpTraits __op__)
{
overload::dilation_like(exact(ima), exact(nbh), cmp, exact(output), __op__);
overload::dilation_dispatch_1(exact(ima), exact(nbh), exact(output), __op__, typename SE::is_decomposable());
}
} // end of namespace mln::morpho::canvas
......
#ifndef MLN_MORPHO_CANVAS_DILATION_LIKE_SPE_HPP
#define MLN_MORPHO_CANVAS_DILATION_LIKE_SPE_HPP
#include <mln/core/algorithm/transpose.hpp>
#include <mln/core/se/rect2d.hpp>
#include <mln/morpho/canvas/dilation_like.hpp>
namespace mln
{
namespace morpho
{
namespace canvas
{
namespace overload
{
// Special case when the SE is a rectangle (seperable)
// Question ? Should we transpose the image to improve
// data locality given than inplace transposition
// might be costly due to cycle detection.
template <class I, class Compare, class J, class OpTraits>
typename std::enable_if<std::is_same<typename I::domain_type, box2d>::value>::type
dilation_like(const Image<I>& ima_, const se::rect2d& nbh, Compare cmp, Image<J>& output, OpTraits __op__)
{
box2d r = nbh.offsets();
const I& ima = exact(ima_);
if (r.shape()[0] == 1)
{
impl::dilate_like_0(ima, nbh, cmp, exact(output), __op__, typename image_has_extension<I>::type());
}
else if (r.shape()[1] == 1)
{
se::rect2d h0(r.pmax[0] - r.pmin[0], 1);
mln_concrete(I) tmp = transpose(ima);
mln_concrete(I) out = imconcretize(tmp);
morpho::canvas::dilation_like(tmp, h0, cmp, out, __op__);
transpose(out, output);
}
else
{
se::rect2d h0(r.pmax[0] - r.pmin[0], 1);
se::rect2d h1(r.pmax[1] - r.pmin[1], 1);
image2d<mln_value(I)> f;
{
mln_concrete(I) tmp = imconcretize(ima);
morpho::canvas::dilation_like(ima, h1, cmp, tmp, __op__);
f = transpose(tmp);
}
{
mln_concrete(I) tmp = imconcretize(f);
morpho::canvas::dilation_like(f, h0, cmp, tmp, __op__);
transpose(tmp, output);
}
}
}
} // end of namespace mln::morpho::canvas::overload
} // end of namespace mln::morpho::canvas
} // end of namespace mln::morpho
} // end of namespace mln
#endif //! MLN_MORPHO_CANVAS_DILATION_LIKE_SPE_HPP
......@@ -53,19 +53,37 @@ namespace mln
namespace internal
{
template <class I, class SE, class Compare>
struct dilate_traits
template <class T, class Compare>
struct dilate_traits_base
{
using support_incremental =
std::integral_constant<bool, std::is_integral<mln_value(I)>::value and
std::is_same<Compare, productorder_less<mln_value(I)>>::value and
(value_traits<mln_value(I)>::quant <= 16)>;
dilate_traits_base(Compare cmp) : accu_sup(cmp), m_cmp(std::move(cmp)) {}
using aggregate_type = accu::accumulators::sup<mln_value(I), Compare>;
using incremental_aggregate_type = accu::accumulators::h_sup<mln_value(I)>;
const mln::accu::accumulators::sup<T, Compare> accu_sup;
auto sup(T x, T y) const { return mln::sup(x, y, m_cmp); }
static constexpr T zero() { return value_traits<T, Compare>::inf(); }
static constexpr mln_value(I) zero() { return value_traits<mln_value(I), Compare>::inf(); }
private:
Compare m_cmp;
};
template <class T, class Compare, class Enable = void>
struct dilate_traits : dilate_traits_base<T, Compare>
{
using dilate_traits_base<T, Compare>::dilate_traits_base;
using support_incremental = std::false_type;
};
template <class T>
struct dilate_traits<T, productorder_less<T>, std::enable_if_t<std::is_integral<T>::value>>
: dilate_traits_base<T, productorder_less<T>>
{
using support_incremental = std::true_type;
dilate_traits(productorder_less<T> cmp) : dilate_traits_base<T, productorder_less<T>>(cmp) {}
const mln::accu::accumulators::h_sup<T> accu_hsup;
};
}
template <class I, class SE, class OutputImage, class Compare>
......@@ -74,7 +92,8 @@ namespace mln
{
mln_entering("mln::morpho::structural::dilate");
morpho::canvas::dilation_like(ima, nbh, cmp, output, internal::dilate_traits<I, SE, Compare>());
internal::dilate_traits<mln_value(I), Compare> __op__(cmp);
morpho::canvas::dilation_like(ima, nbh, output, __op__);
mln_exiting();
return exact(output);
......
......@@ -51,20 +51,36 @@ namespace mln
namespace internal
{
template <class T, class Compare>
struct erode_traits_base
{
erode_traits_base(Compare cmp) : accu_sup(cmp), m_cmp(std::move(cmp)) {}
const mln::accu::accumulators::inf<T, Compare> accu_sup;
auto sup(T x, T y) const { return mln::inf(x, y, m_cmp); }
static constexpr T zero() { return value_traits<T, Compare>::sup(); }
private:
Compare m_cmp;
};
template <class I, class SE, class Compare>
struct erode_traits
template <class T, class Compare, class Enable = void>
struct erode_traits : erode_traits_base<T, Compare>
{
using support_incremental =
std::integral_constant<bool, std::is_integral<mln_value(I)>::value and
std::is_same<Compare, productorder_less<mln_value(I)>>::value and
(value_traits<mln_value(I)>::quant <= 16)>;
using erode_traits_base<T, Compare>::erode_traits_base;
using support_incremental = std::false_type;
};
using aggregate_type = accu::accumulators::inf<mln_value(I), Compare>;
using incremental_aggregate_type = accu::accumulators::h_inf<mln_value(I)>;
template <class T>
struct erode_traits<T, productorder_less<T>, std::enable_if_t<std::is_integral<T>::value>>
: erode_traits_base<T, productorder_less<T>>
{
using support_incremental = std::true_type;
static constexpr mln_value(I) zero() { return value_traits<mln_value(I), Compare>::sup(); }
erode_traits(productorder_less<T> cmp) : erode_traits_base<T, productorder_less<T>>(cmp) {}
const mln::accu::accumulators::h_inf<T> accu_hsup;
};
}
template <class I, class SE, class OutputImage, class Compare>
......@@ -73,7 +89,8 @@ namespace mln
{
mln_entering("mln::morpho::structural::erode");
morpho::canvas::dilation_like(ima, nbh, cmp, output, internal::erode_traits<I, SE, Compare>());
internal::erode_traits<mln_value(I), Compare> __op__(cmp);
morpho::canvas::dilation_like(ima, nbh, output, __op__);
mln_exiting();
return exact(output);
......
......@@ -9,6 +9,8 @@
#include <mln/morpho/canvas/private/dilation_by_periodic_line.hpp>
#include <mln/core/se/disc.hpp>
#include <mln/core/se/rect2d.hpp>
#include <tests/helpers.hpp>
#include <gtest/gtest.h>
......@@ -62,6 +64,42 @@ TEST(Dilation, PeriodicLine2d_vertical_knightmove)
test_dilation_by_periodic_line(mln::point2d{2,-1}, 2);
}
TEST(Dilation, Disc_approximated)
{
mln::box2d domain{ {0,0}, {21, 21}};
mln::image2d<mln::uint8> input(domain, 0);
const mln::image2d<mln::uint8> ref = {
{0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, //
{0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0}, //
{0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0}, //
{0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0}, //
{0, 0, 0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0}, //
{0, 0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0}, //
{0, 0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0}, //
{0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0}, //
{0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0}, //
{0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0}, //
{0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0}, //
{0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0}, //
{0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0}, //
{0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0}, //
{0, 0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0}, //
{0, 0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0}, //
{0, 0, 0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0}, //
{0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0}, //
{0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0}, //
{0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0}, //
{0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, //
};
input.at(10, 10) = 1;
auto output = mln::morpho::structural::dilate(input, mln::se::disc(9));
ASSERT_IMAGES_EQ(ref, output);
}
TEST(Dilation, Disc_euclidean)
{
mln::box2d domain{ {0,0}, {21, 21}};
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment