Commit 25f8a9e3 by Edwin Carlinet

### Add local minima labeling.

parent 7861a204
 #ifndef MLN_LABELING_LOCAL_MINIMA_HPP # define MLN_LABELING_LOCAL_MINIMA_HPP # include # include # include # include # include namespace mln { namespace labeling { /// Compute and labelize the local mimima of an image /// /// \param[in] input Input image /// \param[in] nbh neighborhood /// \param[out] nlabel Number of minima /// \param[in] cmp (optional) Strict weak ordering comparison function for values template > mln_ch_value(I, Label_t) local_minima(const Image& input, const Neighborhood& nbh, int& nlabel, Compare cmp = Compare()); /// Compute and labelize the local maxima of an image /// /// \param[in] input Input image /// \param[in] nbh neighborhood /// \param[out] nlabel Number of maxima template mln_ch_value(I, Label_t) local_maxima(const Image& input, const Neighborhood& nbh, int& nlabel); /******************************************/ /**** Implementation ****/ /******************************************/ namespace details { template Point zfindroot(Parent& parent, Point& p) { auto& par = parent(p); if (par != p) p = zfindroot(parent, par); return p; } // Output must initialized to Label_t(-1) template int local_minima(const I& input, const N& nbh, O& output, Compare cmp) { using Label_t = mln_value(O); constexpr Label_t kUnseen = static_cast(-1); mln_ch_value(I, mln_point(I)) parent = imchvalue(input); mln_pixter(pxIn, input); mln_pixter(pxOut, output); mln_pixter(pxPar, parent); mln_iter(nxIn, nbh(pxIn)); mln_iter(nxOut, nbh(pxOut)); mln_iter(nxPar, nbh(pxPar)); { // Forward scan mln_forall(pxIn, pxOut, pxPar) { auto p = pxPar->point(); auto p_input_value = pxIn->val(); // Make set auto root = p; bool is_possible_minimum = true; mln_forall(nxIn, nxOut, nxPar) { if (nxOut->val() == kUnseen) continue; auto n_input_value = nxIn->val(); if (cmp(n_input_value, p_input_value)) // input(n) < input(p) { is_possible_minimum = false; } else if (cmp(p_input_value, n_input_value)) // input(p) < input(n) -> n is not a local minimum { const auto r = zfindroot(parent, nxPar->val()); output(r) = false; } else // Flat zone linking { const auto n_root = zfindroot(parent, nxPar->val()); if (n_root != root) { is_possible_minimum &= output(n_root); auto roots = std::minmax(root, n_root); parent(roots.second) = roots.first; root = roots.first; } } } pxPar->val() = root; pxOut->val() = is_possible_minimum; output(root) = is_possible_minimum; } } int n_label = 0; // Labelisation { mln_forall(pxOut, pxPar) { if (pxOut->val()) { if (pxPar->point() == pxPar->val()) // New minimum pxOut->val() = ++n_label; else { auto r = zfindroot(parent, pxPar->val()); pxOut->val() = output(r); } } } } mln_assertion((n_label <= value_traits::max()) && "Overflow detected (the number of label has exceed the label type capacity"); return n_label; } } template mln_ch_value(I, Label_t) local_minima(const Image& input_, const Neighborhood& nbh_, int& nlabel, Compare cmp) { mln_entering("mln::labeling::local_minima"); static_assert(std::is_integral::value && !std::is_same::value, "Label type must a non-bool integral type"); constexpr Label_t kUnseen = static_cast(-1); const I& input = exact(input_); const N& nbh = exact(nbh_); mln_ch_value(I, Label_t) output = imchvalue(input).adjust(nbh).init(kUnseen); if (extension::need_adjust(output, nbh)) { auto out = extension::add_value_extension(output, kUnseen); nlabel = details::local_minima(input, nbh, out, cmp); } else { mln::extension::fill(output, kUnseen); nlabel = details::local_minima(input, nbh, output, cmp); } return output; } template mln_ch_value(I, Label_t) local_maxima(const Image& input, const Neighborhood& nbh, int& nlabel) { return local_minima(input, nbh, nlabel, std::greater ()); } } // end of namespace mln::labeling } // end of namespace mln #endif //!MLN_LABELING_LOCAL_MINIMA_HPP
 ... ... @@ -2,3 +2,4 @@ set(test_prefix "UTLabeling_") add_core_test(\${test_prefix}blobs blobs.cpp) add_core_test(\${test_prefix}rag rag.cpp) add_core_test(\${test_prefix}local_extrema local_extrema.cpp)
 #include #include #include #include #include #include using namespace mln; TEST(Morpho, local_minima) { const mln::image2d input = { { 2, 2, 2, 2, 2}, {40, 30, 30, 30, 40}, {40, 20, 20, 20, 40}, {40, 40, 20, 40, 40}, { 1, 5, 20, 5, 1}}; const mln::image2d ref = { { 1, 1, 1, 1, 1}, { 0, 0, 0, 0, 0}, { 0, 0, 0, 0, 0}, { 0, 0, 0, 0, 0}, { 2, 0, 0, 0, 3}}; int nlabel; auto res = mln::labeling::local_minima(input, mln::c4, nlabel); ASSERT_EQ(3, nlabel); ASSERT_IMAGES_EQ(ref, res); } TEST(Morpho, local_maxima) { const mln::image2d input = { { 2, 2, 2, 2, 2}, {40, 30, 30, 30, 40}, {40, 20, 20, 20, 40}, {40, 40, 20, 40, 40}, { 1, 5, 20, 5, 1}}; const mln::image2d ref = { { 0, 0, 0, 0, 0}, { 1, 0, 0, 0, 2}, { 1, 0, 0, 0, 2}, { 1, 1, 0, 2, 2}, { 0, 0, 0, 0, 0}}; int nlabel; auto res = mln::labeling::local_maxima(input, mln::c4, nlabel); ASSERT_EQ(2, nlabel); ASSERT_IMAGES_EQ(ref, res); }
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