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

Add directional hierarchical queue

parent 11aa7c35
Pipeline #27831 failed with stages
in 19 minutes and 34 seconds
......@@ -4,18 +4,18 @@
namespace mln::morpho
{
template <typename V, typename Nodemap, typename L>
auto horizontal_cut_labelization_from(const component_tree<V>& t, Nodemap nm, V th, ::ranges::span<L> vals);
template <typename V, typename Nodemap, typename Th, typename L>
auto horizontal_cut_labelization_from(const component_tree<V>& t, Nodemap nm, Th th, ::ranges::span<L> vals);
template <typename V, typename Nodemap, typename L>
auto horizontal_cut_labelization_from(const component_tree<V>& t, Nodemap nm, V th, const std::vector<L>& vals);
template <typename V, typename Nodemap, typename Th, typename L>
auto horizontal_cut_labelization_from(const component_tree<V>& t, Nodemap nm, Th th, const std::vector<L>& vals);
/******************
* Implementation *
******************/
template <typename V, typename Nodemap, typename L>
auto horizontal_cut_labelization_from(const component_tree<V>& t, Nodemap nm, V th, ::ranges::span<L> vals)
template <typename V, typename Nodemap, typename Th, typename L> requires(std::is_convertible_v<Th, V>)
auto horizontal_cut_labelization_from(const component_tree<V>& t, Nodemap nm, Th th, ::ranges::span<L> vals)
{
static_assert(mln::is_a_v<Nodemap, mln::details::Image>);
static_assert(std::is_same_v<image_value_t<Nodemap>, int>);
......@@ -39,8 +39,8 @@ namespace mln::morpho
return lbl;
}
template <typename V, typename Nodemap, typename L>
auto horizontal_cut_labelization_from(const component_tree<V>& t, Nodemap nm, V th, const std::vector<L>& vals)
template <typename V, typename Nodemap, typename Th, typename L> requires(std::is_convertible_v<Th, V>)
auto horizontal_cut_labelization_from(const component_tree<V>& t, Nodemap nm, Th th, const std::vector<L>& vals)
{
return horizontal_cut_labelization_from(t, nm, th, ::ranges::make_span(vals.data(), vals.size()));
}
......
#pragma once
#include <mln/core/concepts/image.hpp>
#include <mln/core/range/foreach.hpp>
#include <mln/morpho/private/hvector_unbounded.hpp>
#include <array>
#include <memory>
namespace mln::morpho::details
{
/// \brief Directional hierarchical queue
/// \tparam I The image the edges are calculated from
/// \tparam N The neighborhood used to compute the edges
/// \tparam F The distance function
template <typename I, typename N, typename F>
class directional_hqueue
{
static_assert(is_a_v<I, mln::details::Image>);
using V = image_value_t<I>;
using Key = std::invoke_result_t<F, V, V>;
using Point = image_point_t<I>;
using queue_type = hvectors_unbounded<Point>;
public:
/// \brief Constructor
directional_hqueue() noexcept;
/// \brief Insert an point in the directional hierarchical queue
/// \param dir The hierarchical queue index (from after_offset() in N)
/// \param w The weight of the edge
/// \param p The point the edge weight is calculated from
void insert(std::size_t dir, Key w, Point p) noexcept;
/// \brief Return the tuple (p, q, w) where p and q are the vertex of the edge and w its weight
std::tuple<Point, Point, Key> pop() noexcept;
/// \brief Return the lower weight of the queue
Key current_level() const noexcept;
/// \brief Return true if the directional queue is empty
bool empty() const noexcept;
private:
static_assert(std::is_integral_v<Key> && std::is_unsigned_v<Key>);
static_assert(sizeof(Key) <= 2);
private:
static constexpr std::size_t m_ndir = N::after_offsets().size();
static constexpr std::size_t m_nlevels = 1 << std::numeric_limits<Key>::digits;
std::array<std::shared_ptr<queue_type>, 2> m_queues;
std::size_t m_current_dir;
Key m_current_level;
std::size_t m_size;
};
/******************
* Implementation *
******************/
template <typename I, typename N, typename F>
directional_hqueue<I, N, F>::directional_hqueue() noexcept
: m_current_dir(0)
, m_current_level(0)
, m_size(0)
{
for (std::size_t i = 0; i < m_ndir; i++)
m_queues[i] = std::make_shared<queue_type>(m_nlevels);
}
template <typename I, typename N, typename F>
void directional_hqueue<I, N, F>::insert(std::size_t dir, Key w, Point p) noexcept
{
// Update the current level and the current dir to keep the queue sorted
if (m_size == 0 || w < m_current_level)
{
m_current_dir = dir;
m_current_level = w;
}
m_queues[dir]->push_front(w, p);
m_size++;
}
template <typename I, typename N, typename F>
std::tuple<typename directional_hqueue<I, N, F>::Point, typename directional_hqueue<I, N, F>::Point,
typename directional_hqueue<I, N, F>::Key>
directional_hqueue<I, N, F>::pop() noexcept
{
assert(m_size > 0);
const auto p = m_queues[m_current_dir]->pop_front(m_current_level);
const auto q = p + N::after_offsets()[m_current_dir];
const auto w = m_current_level;
m_size--;
// Update the current level and the current dir if needed
if (m_size > 0 && m_queues[m_current_dir]->empty(m_current_level))
{
std::size_t lvl = m_queues[0]->lower_bound(m_current_level);
std::size_t dir = 0;
for (std::size_t i = 1; i < m_ndir; i++)
{
std::size_t tmp = m_queues[i]->lower_bound(m_current_level);
if (tmp < lvl && tmp < m_nlevels)
{
lvl = tmp;
dir = i;
}
}
m_current_level = lvl;
m_current_dir = dir;
}
return {p, q, w};
}
template <typename I, typename N, typename F>
typename directional_hqueue<I, N, F>::Key directional_hqueue<I, N, F>::current_level() const noexcept
{
assert(m_size > 0);
return m_current_level;
}
template <typename I, typename N, typename F>
bool directional_hqueue<I, N, F>::empty() const noexcept
{
return m_size == 0;
}
} // namespace mln::morpho::details
\ No newline at end of file
......@@ -21,6 +21,7 @@ add_core_test(${test_prefix}depth_first depthfirst.cpp)
add_core_test(${test_prefix}maxtree maxtree.cpp)
add_core_test(${test_prefix}alphatree alphatree.cpp)
add_core_test(${test_prefix}cut cut.cpp)
add_core_test(${test_prefix}private_directional_hqueue directional_hqueue.cpp)
......
......@@ -52,7 +52,7 @@ TEST(Morpho, AlphaTree)
{107, 87, 118, 109, 167}, //
{107, 73, 125, 157, 117}, //
};
auto [t, node_map] = mln::morpho::alphatree(ima, mln::c4);
auto [t, node_map] = mln::morpho::alphatree(ima, mln::c4, [](const auto& a, const auto& b) -> std::uint8_t { return mln::functional::l2dist_t<>()(a, b);});
mln::image2d<int> ref0 = {
{0, 1, 2, 3, 4}, //
......@@ -145,15 +145,15 @@ TEST(Morpho, AlphaTreeRGB8)
};
auto [t, nm] = mln::morpho::alphatree(
ima, mln::c4, [](const auto& a, const auto& b) -> int { return mln::functional::l2dist_t<>()(a, b); });
ima, mln::c4, [](const auto& a, const auto& b) -> std::uint16_t { return mln::functional::l2dist_t<>()(a, b); });
auto rng = std::vector<int>(t.parent.size());
std::iota(rng.begin(), rng.end(), 0);
ASSERT_IMAGES_EQ_EXP(mln::morpho::horizontal_cut_labelization_from(t, nm, 0, rng), nm);
ASSERT_IMAGES_EQ_EXP(mln::morpho::horizontal_cut_labelization_from(t, nm, 8, rng), ref_8);
ASSERT_IMAGES_EQ_EXP(mln::morpho::horizontal_cut_labelization_from(t, nm, 16, rng), ref_16);
ASSERT_IMAGES_EQ_EXP(mln::morpho::horizontal_cut_labelization_from(t, nm, 17, rng), ref_17);
ASSERT_IMAGES_EQ_EXP(mln::morpho::horizontal_cut_labelization_from(t, nm, 18, rng), ref_18);
ASSERT_IMAGES_EQ_EXP(mln::morpho::horizontal_cut_labelization_from(t, nm, 24, rng), ref_24);
ASSERT_IMAGES_EQ_EXP(mln::morpho::horizontal_cut_labelization_from(t, nm, 32, rng), ref_32);
ASSERT_IMAGES_EQ_EXP(mln::morpho::horizontal_cut_labelization_from(t, nm, 36, rng), ref_36);
ASSERT_IMAGES_EQ_EXP(mln::morpho::horizontal_cut_labelization_from(t, nm, 0u, rng), nm);
ASSERT_IMAGES_EQ_EXP(mln::morpho::horizontal_cut_labelization_from(t, nm, 8u, rng), ref_8);
ASSERT_IMAGES_EQ_EXP(mln::morpho::horizontal_cut_labelization_from(t, nm, 16u, rng), ref_16);
ASSERT_IMAGES_EQ_EXP(mln::morpho::horizontal_cut_labelization_from(t, nm, 17u, rng), ref_17);
ASSERT_IMAGES_EQ_EXP(mln::morpho::horizontal_cut_labelization_from(t, nm, 18u, rng), ref_18);
ASSERT_IMAGES_EQ_EXP(mln::morpho::horizontal_cut_labelization_from(t, nm, 24u, rng), ref_24);
ASSERT_IMAGES_EQ_EXP(mln::morpho::horizontal_cut_labelization_from(t, nm, 32u, rng), ref_32);
ASSERT_IMAGES_EQ_EXP(mln::morpho::horizontal_cut_labelization_from(t, nm, 36u, rng), ref_36);
}
\ No newline at end of file
......@@ -41,40 +41,4 @@ TEST(Morpho, AlphaTreeCutMeanLabelization)
ASSERT_IMAGES_EQ_EXP(mln::morpho::horizontal_cut_labelization_from(t, nm, 1, val), cut_1);
ASSERT_IMAGES_EQ_EXP(mln::morpho::horizontal_cut_labelization_from(t, nm, 2, val), cut_2);
ASSERT_IMAGES_EQ_EXP(mln::morpho::horizontal_cut_labelization_from(t, nm, 3, val), cut_3);
}
TEST(Morpho, AlphaTreeCutRangeLabelization)
{
mln::image2d<int> ima = {
{1, 1, 5}, //
{2, 5, 6}, //
{0, 1, 4} //
};
mln::image2d<int> cut_1 = {
{5, 5, 4}, //
{5, 4, 4}, //
{3, 3, 6} //
};
mln::image2d<int> cut_2 = {
{2, 2, 1}, //
{2, 1, 1}, //
{2, 2, 1} //
};
mln::image2d<int> cut_3 = {
{0, 0, 0}, //
{0, 0, 0}, //
{0, 0, 0} //
};
auto [t, nm] = mln::morpho::alphatree(ima, mln::c4);
auto val = std::vector<int>(t.parent.size());
std::iota(val.begin(), val.end(), 0);
ASSERT_IMAGES_EQ_EXP(mln::morpho::horizontal_cut_labelization_from(t, nm, 0, val), nm);
ASSERT_IMAGES_EQ_EXP(mln::morpho::horizontal_cut_labelization_from(t, nm, 1, val), cut_1);
ASSERT_IMAGES_EQ_EXP(mln::morpho::horizontal_cut_labelization_from(t, nm, 2, val), cut_2);
ASSERT_IMAGES_EQ_EXP(mln::morpho::horizontal_cut_labelization_from(t, nm, 3, val), cut_3);
}
\ No newline at end of file
#include <mln/morpho/private/directional_hqueue.hpp>
#include <mln/core/functional_ops.hpp>
#include <mln/core/image/ndimage.hpp>
#include <mln/core/neighborhood/c4.hpp>
#include <gtest/gtest.h>
TEST(Morpho, DirectionalHQueue)
{
mln::image2d<std::uint8_t> ima = {
{10, 15, 32}, //
{12, 16, 8}, //
{7, 28, 20} //
};
auto dist = [](std::uint8_t a, std::uint8_t b) -> std::uint8_t { return mln::functional::l2dist_t<>()(a, b); };
using F = decltype(dist);
using V = mln::image_value_t<decltype(ima)>;
using W = std::invoke_result_t<F, V, V>;
constexpr std::array<W, 12> ref = {1, 2, 4, 5, 5, 8, 8, 12, 12, 17, 21, 24};
auto hqueue = mln::morpho::details::directional_hqueue<decltype(ima), mln::c4_t, F>();
mln_foreach (auto p, ima.domain())
{
std::size_t i = 0;
for (auto q : mln::c4.after(p))
{
if (ima.domain().has(q))
hqueue.insert(i, dist(ima(p), ima(q)), p);
i++;
}
}
std::size_t n = 0;
while (!hqueue.empty())
{
auto [p, q, w] = hqueue.pop();
auto rw = ref[n];
ASSERT_EQ(w, rw);
n++;
}
ASSERT_EQ(n, 12);
}
\ No newline at end of file
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