Commit a843b777 authored by Baptiste Esteban's avatar Baptiste Esteban
Browse files

Merge branch 'development/alphatree-tests' of...

Merge branch 'development/alphatree-tests' of https://gitlab.lrde.epita.fr/olena/pylene into development/alphatree
parents d4dd2160 5af5eb70
Pipeline #28130 failed with stage
in 24 minutes and 54 seconds
......@@ -179,15 +179,15 @@ namespace mln::morpho
//
//
template <class E, class I, class W>
template <class E, class I, class W, class M>
std::size_t alphatree_compute_hierarchy(E& edges, I node_map, //
std::size_t node_count, //
std::vector<int>& par, //
std::vector<W>& levels)
std::vector<W>& levels, //
std::vector<M>* mst)
{
static_assert(mln::is_a<I, mln::details::Image>());
std::vector<int> zpar(node_count); // Parent of ufind structure
std::vector<int> links(node_count); // Node index in the alpha tree
......@@ -230,64 +230,119 @@ namespace mln::morpho
par.push_back(new_root_id);
levels.push_back(w);
}
par[rp_root] = new_root_id;
par[rq_root] = new_root_id;
links[rp] = new_root_id;
if (mst != nullptr)
mst->push_back({p, q, w});
}
}
return node_count;
}
} // namespace internal
template <class I, class N, class F>
std::pair<component_tree<std::invoke_result_t<F, image_value_t<I>, image_value_t<I>>>, image_ch_value_t<I, int>> //
alphatree(I input, N nbh, F distance)
{
static_assert(mln::is_a<I, mln::details::Image>());
static_assert(mln::is_a<N, mln::details::Neighborhood>());
static_assert(::ranges::cpp20::invocable<F, image_value_t<I>, image_value_t<I>>);
template <class W>
std::pair<std::vector<int>, std::vector<W>> canonize_component_tree(const std::vector<int>& par, //
const std::vector<W>& levels, //
std::size_t node_count)
{
std::vector<int> canonized_par;
std::vector<W> canonized_levels;
using V = image_value_t<I>;
using P = image_point_t<I>;
using W = std::invoke_result_t<F, V, V>;
// Root initialization
canonized_par.push_back(0);
canonized_levels.push_back(levels[0]);
static_assert(::concepts::totally_ordered<W>);
std::vector<int> translation_map(node_count);
// 1. Get the list of edges
auto edges = internal::alphatree_compute_edges(std::move(input), std::move(nbh), std::move(distance));
translation_map[0] = 0;
std::size_t count = 1;
std::size_t flatzones_count;
image_ch_value_t<I, int> node_map = imchvalue<int>(input).set_init_value(-1);
{
image_ch_value_t<I, P> zpar = imchvalue<P>(input);
// 2. Compute flat zone of the image
internal::alphatree_compute_flatzones(edges, zpar);
// Build canonized component tree
for (std::size_t i = 1; i < node_count; ++i)
{
if (levels[i] != levels[par[i]]) // Keep the node: Update tree
{
translation_map[i] = count++;
canonized_par.push_back(translation_map[par[i]]);
canonized_levels.push_back(levels[i]);
}
else // Deleted node: retrieve the parent translation
translation_map[i] = translation_map[par[i]];
}
// 3. Compute a node_id for each flat zone
flatzones_count = internal::alphatree_create_nodemap(node_map, zpar);
return {canonized_par, canonized_levels};
}
std::size_t node_count = flatzones_count;
std::vector<int> par(node_count);
std::vector<W> levels(node_count, 0);
// 4. Compute the hierarchy
template <class I, class N, class F,
class M = edge_t<image_point_t<I>, std::invoke_result_t<F, image_value_t<I>, image_value_t<I>>>>
std::pair<component_tree<std::invoke_result_t<F, image_value_t<I>, image_value_t<I>>>, image_ch_value_t<I, int>> //
__alphatree(I input, N nbh, F distance, std::vector<M>* mst = nullptr, bool canonize_tree = true)
{
std::iota(std::begin(par), std::end(par), 0);
node_count = internal::alphatree_compute_hierarchy(edges, node_map, node_count, par, levels);
}
static_assert(mln::is_a<I, mln::details::Image>());
static_assert(mln::is_a<N, mln::details::Neighborhood>());
static_assert(::ranges::cpp20::invocable<F, image_value_t<I>, image_value_t<I>>);
using V = image_value_t<I>;
using P = image_point_t<I>;
using W = std::invoke_result_t<F, V, V>;
static_assert(::concepts::totally_ordered<W>);
static_assert(std::is_same<M, edge_t<P, W>>());
// 1. Get the list of edges
auto edges = internal::alphatree_compute_edges(std::move(input), std::move(nbh), std::move(distance));
// 5. Parent / levels are ordered from leaves to root, we need to reverse
internal::alphatree_reorder_nodes(par.data(), levels.data(), node_count);
std::size_t flatzones_count;
image_ch_value_t<I, int> node_map = imchvalue<int>(input).set_init_value(-1);
{
image_ch_value_t<I, P> zpar = imchvalue<P>(input);
// 2. Compute flat zone of the image
internal::alphatree_compute_flatzones(edges, zpar);
// 6. Update the node_map
mln::for_each(node_map, [node_count](int& id) { id = static_cast<int>(node_count) - id - 1; });
// 3. Compute a node_id for each flat zone
flatzones_count = internal::alphatree_create_nodemap(node_map, zpar);
}
component_tree<W> t;
t.parent = std::move(par);
t.values = std::move(levels);
std::size_t node_count = flatzones_count;
return {std::move(t), std::move(node_map)};
std::vector<int> par(node_count);
std::vector<W> levels(node_count, 0);
// 4. Compute the hierarchy
{
std::iota(std::begin(par), std::end(par), 0);
node_count = internal::alphatree_compute_hierarchy(edges, node_map, node_count, par, levels, mst);
}
// 5. Parent / levels are ordered from leaves to root, we need to reverse
internal::alphatree_reorder_nodes(par.data(), levels.data(), node_count);
// Optional tree canonization: remove useless nodes
if (canonize_tree)
{
auto [canonized_par, canonized_levels] = internal::canonize_component_tree(par, levels, node_count);
par = canonized_par;
levels = canonized_levels;
node_count = par.size();
}
// 6. Update the node_map
mln::for_each(node_map, [node_count](int& id) { id = static_cast<int>(node_count) - id - 1; });
component_tree<W> t;
t.parent = std::move(par);
t.values = std::move(levels);
return {std::move(t), std::move(node_map)};
}
} // namespace internal
template <class I, class N, class F>
std::pair<component_tree<std::invoke_result_t<F, image_value_t<I>, image_value_t<I>>>, image_ch_value_t<I, int>> //
alphatree(I input, N nbh, F distance)
{
return internal::__alphatree(input, nbh, distance);
}
} // namespace mln::morpho
\ No newline at end of file
......@@ -112,6 +112,63 @@ TEST(Morpho, AlphaTreeParentRelation)
ASSERT_TRUE(static_cast<int>(i) >= t.parent[i]);
}
TEST(Morpho, AlphaTreeCanonized)
{
mln::image2d<int> ima = {
{4, 0, 0, 1}, //
{5, 0, 8, 1}, //
{6, 0, 9, 1}, //
{7, 0, 10, 1}, //
{2, 3, 4, 5}, //
};
auto [t, _] = mln::morpho::alphatree(ima, mln::c4);
for (std::size_t i = 1; i < t.parent.size(); ++i)
ASSERT_TRUE(t.values[i] != t.values[t.parent[i]]);
}
TEST(Morpho, AlphaTreeMST)
{
mln::image2d<int> ima = {
{4, 0, 0, 1}, //
{5, 0, 8, 1}, //
{6, 0, 9, 1}, //
{7, 0, 10, 1}, //
{2, 3, 4, 5}, //
};
std::vector<mln::morpho::internal::edge_t<mln::image_point_t<mln::image2d<int>>, std::float_t>> expected_mst = {
{{0, 2}, {0, 3}, 1.}, //
{{2, 4}, {3, 4}, 1.}, //
{{1, 4}, {2, 4}, 1.}, //
{{0, 4}, {1, 4}, 1.}, //
{{2, 2}, {2, 3}, 1.}, //
{{0, 0}, {0, 1}, 1.}, //
{{2, 0}, {3, 0}, 1.}, //
{{0, 1}, {0, 2}, 1.}, //
{{2, 1}, {2, 2}, 1.}, //
{{1, 3}, {1, 4}, 3.}, //
{{0, 0}, {1, 0}, 4.}, //
{{2, 3}, {2, 4}, 6.} //
};
std::vector<mln::morpho::internal::edge_t<mln::image_point_t<mln::image2d<int>>, std::float_t>> mst;
auto [t, _] = mln::morpho::internal::__alphatree(
ima, mln::c4, [](const auto& a, const auto& b) -> std::float_t { return mln::functional::l2dist_t<>()(a, b); },
&mst);
for (std::size_t i = 0; i < expected_mst.size(); ++i)
{
ASSERT_EQ(expected_mst[i].p, mst[i].p);
ASSERT_EQ(expected_mst[i].q, mst[i].q);
ASSERT_EQ(expected_mst[i].w, mst[i].w);
}
ASSERT_EQ(expected_mst.size(), mst.size());
}
TEST(Morpho, AlphaTreeRGB8Uint16Distance)
{
const mln::image2d<mln::rgb8> ima = {
......
Supports Markdown
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