Commit 0697169a by Baptiste Esteban

### Make horizontal_cut member of component_tree + update doc

parent 38cb3771
Pipeline #28140 failed with stage
in 19 minutes and 40 seconds
 ... ... @@ -10,7 +10,7 @@ Alpha Tree Compute the alpha tree (also known as quasi-flat zone hierarchy) and returns a pair (tree, node_map). See :doc:component_tree for more information about the representation of tree. representation of tree. The implementation is based on the Kruskal algorithm [Naj13]_. :param input: The input image :param nbh: The neighborhood ... ... @@ -54,24 +54,29 @@ between a node of the tree and a pixel of the image being represented by blue da Example ------- This example is used to generate the grayscale lena cut with a threshold of 3 below. :: #include #include #include #include #include // for horizontal_cut_labelization_from mln::image2d input = ...; // Compute the alpha tree auto [tree, node_map] = mln::morpho::alphatree(input, mln::c4); // Compute an attribute (for example the average pixels value at each node, as below) auto mean = t.compute_attribute_on_values(node_map, input, mln::accu::accumulators::mean()); auto mean = t.compute_attribute_on_values(node_map, input, mln::accu::accumulators::mean()); // Making an horizontal cut of the tree auto th = ...; auto cut = mln::morpho::horizontal_cut_labelization_from(t, node_map, th, mean); const auto threshold = 3; // Threshold of the horizontal cut, that means the lowest alpha in the cut auto nodemap_cut = t.horizontal_cut(threshold, node_map); // Return a new nodemap associated to the cut // Labelizing the cut with the mean values of each node auto out = t.reconstruct_from(nodemap_cut, ranges::make_span(mean)); // Using range-v3 span .. list-table:: ... ... @@ -93,3 +98,5 @@ Complexity References ---------- .. [Naj13] Laurent Najman, Jean Cousty, and Benjamin Perret (2013). Playing with kruskal: algorithms for morphological trees in edge-weighted graphs. *International Symposium on Mathematical Morphology and Its Applications to Signal and Image Processing*. Springer, Berlin, Heidelberg. 135-146 \ No newline at end of file
 ... ... @@ -285,17 +285,14 @@ Horizontal cut When the tree is a hierarchy of partition, such as the :doc:alphatree, it is possible to make an horizontal cut of this tree. * Include :file: .. cpp:function:: I horizontal_cut(const T threshold, I nodemap) const I horizontal_cut_from_levels(const T threshold, I nodemap, ::ranges::span levels) const .. cpp:function:: auto horizontal_cut_labelization_from(const component_tree& t, Nodemap nm, V th, const std::vector& vals) auto horizontal_cut_labelization_from(const component_tree& t, Nodemap nm, V th, ::ranges::span vals) Make an horizontal cut at threshold threshold of the tree and return the nodemap associated to the cut. Make an horizontal cut at threshold th of the tree and labelize the node on a reconstructed image with the value vals. :param t: The tree. :param nm: The node map. :param th: The threshold of the cut. :param vals: the value assigned to each node of the tree for the labelization. :param threshold: The threshold of the horizontal cut :param nodemap: An image thats maps point -> node id :param levels: (Optional) The altitude of each node in the tree (for example the :math:\alpha associated to each node for the alphatree). A complete example ------------------ ... ...
 ... ... @@ -6,12 +6,11 @@ #include #include #include #include #include template void process_example(const mln::image2d& img, const std::string& cut_filename, double threshold) void process_example(const mln::image2d& img, const std::string& cut_filename, const double threshold) { // 2. Build the alphatree auto [t, nm] = mln::morpho::alphatree(img, mln::c4); ... ... @@ -22,7 +21,10 @@ void process_example(const mln::image2d& img, const std::string& cut_filename auto mean = t.compute_attribute_on_values(nm, img, mln::accu::accumulators::mean()); // 4. Compute a cut of the alphatree auto cut = mln::morpho::horizontal_cut_labelization_from(t, nm, th_value_type(threshold), mean); auto cut_nm = t.horizontal_cut(th_value_type(threshold), nm); // 5. Labelize the cut auto cut = t.reconstruct_from(cut_nm, ::ranges::make_span(mean)); // 5. Save the output cut mln::io::imsave(mln::view::cast(cut), cut_filename); ... ...
 ... ... @@ -2,6 +2,7 @@ #include #include #include #include #include ... ... @@ -26,13 +27,12 @@ namespace mln::morpho class component_tree; template <> class component_tree { public: using node_id_type = int; using node_map_t = int; using node_map_t = int; /// \brief Filter the tree given a predicate that removes some nodes according to the selected strategy. ... ... @@ -67,7 +67,6 @@ namespace mln::morpho void filter(ct_filtering strategy, I node_map, F pred); /// \brief Compute the depth attribute over a tree std::vector compute_depth() const; ... ... @@ -106,7 +105,14 @@ namespace mln::morpho std::vector>::type> // compute_attribute_on_pixels(I node_map, J values, Accu acc); /// \brief Compute the horizontal cut of a hierarchie at level threshold and return a nodemap /// valued with the node indices of the lowest nodes satisfying levels[n] > threshold /// /// \param threshold The threshold of the cut /// \param nodemap Image point -> node_id mapping /// \param levels Altitude of each node in the tree template I horizontal_cut_from_levels(const T threshold, I nodemap, ::ranges::span levels) const; /// \brief Reconstruct an image from an attribute map ... ... @@ -117,7 +123,6 @@ namespace mln::morpho image_ch_value_t reconstruct_from(I node_map, ::ranges::span values) const; using node_t = int; std::vector parent; ... ... @@ -137,6 +142,11 @@ namespace mln::morpho class component_tree : public component_tree { public: template I horizontal_cut(const T threshold, I nodemap) const { return this->horizontal_cut_from_levels(threshold, nodemap, ::ranges::make_span(values.data(), values.size())); } template image_ch_value_t, T> reconstruct(I&& node_map) ... ... @@ -167,7 +177,7 @@ namespace mln::morpho template void component_tree::update_node_map(I node_map, F pred) const { mln_foreach(auto& id, node_map.values()) mln_foreach (auto& id, node_map.values()) { if (id > 0 && !pred(id)) id = this->parent[id]; ... ... @@ -175,7 +185,6 @@ namespace mln::morpho } template void component_tree::filter(ct_filtering strategy, F pred) { ... ... @@ -213,7 +222,7 @@ namespace mln::morpho // Propagate upward for (std::size_t i = n - 1; i > 0; --i) { pass[i] = pass[i] || pred(i); pass[i] = pass[i] || pred(i); pass[parent[i]] = pass[parent[i]] || pass[i]; } ... ... @@ -260,7 +269,7 @@ namespace mln::morpho // Propagate upward for (std::size_t i = n - 1; i > 0; --i) { pass[i] = pass[i] || pred(static_cast(i)); pass[i] = pass[i] || pred(static_cast(i)); pass[parent[i]] = pass[parent[i]] || pass[i]; } this->filter_direct(pass); ... ... @@ -270,9 +279,6 @@ namespace mln::morpho } template std::vector>::type> component_tree::compute_attribute_on_points(I node_map, Accu acc) ... ... @@ -288,7 +294,7 @@ namespace mln::morpho std::vector attr(parent.size(), a); // Accumulate for each point mln_foreach(auto px, node_map.pixels()) mln_foreach (auto px, node_map.pixels()) attr[px.val()].take(px.point()); ... ... @@ -322,7 +328,7 @@ namespace mln::morpho // Accumulate for each point auto zz = mln::view::zip(node_map, input); mln_foreach((auto [node_id, val]), zz.values()) mln_foreach ((auto [node_id, val]), zz.values()) attr[node_id].take(val); ... ... @@ -373,7 +379,23 @@ namespace mln::morpho return out; } template I component_tree::horizontal_cut_from_levels(const T threshold, I nodemap, ::ranges::span levels) const { mln_entering("mln::morpho::component_tree::horizontal_cut_from_levels"); auto root_cut_cc = std::vector(parent.size()); for (std::size_t node = 0; node < parent.size(); ++node) { int parent_node = parent[node]; root_cut_cc[node] = levels[parent_node] > threshold ? node : root_cut_cc[parent_node]; } auto out = mln::clone(nodemap); mln_foreach (auto px, out.pixels()) out(px.point()) = root_cut_cc[px.val()]; return out; } template image_ch_value_t component_tree::reconstruct_from(I node_map, ::ranges::span values) const ... ... @@ -390,4 +412,4 @@ namespace mln::morpho } } // namespace mln::morpho:: } // namespace mln::morpho
 #pragma once #include namespace mln::morpho { template auto horizontal_cut_labelization_from(const component_tree& t, Nodemap nm, Th th, ::ranges::span vals); template auto horizontal_cut_labelization_from(const component_tree& t, Nodemap nm, Th th, const std::vector& vals); /****************** * Implementation * ******************/ template requires(std::is_convertible_v) auto horizontal_cut_labelization_from(const component_tree& t, Nodemap nm, Th th, ::ranges::span vals) { static_assert(mln::is_a_v); static_assert(std::is_same_v, int>); using ValueType = std::remove_cv_t; assert(static_cast(vals.size()) == t.parent.size()); image_ch_value_t lbl = imchvalue(nm); std::size_t n = t.parent.size(); // Root of cut connected component std::vector root_cut_cc(n); for (std::size_t node = 0; node < n; ++node) { std::size_t parent_node = t.parent[node]; root_cut_cc[node] = t.values[parent_node] > th ? node : root_cut_cc[parent_node]; } // Reconstruction of image mln_foreach (auto px, nm.pixels()) lbl(px.point()) = vals[root_cut_cc[px.val()]]; return lbl; } template requires(std::is_convertible_v) auto horizontal_cut_labelization_from(const component_tree& t, Nodemap nm, Th th, const std::vector& vals) { return horizontal_cut_labelization_from(t, nm, th, ::ranges::make_span(vals.data(), vals.size())); } } // namespace mln::morpho \ No newline at end of file
 ... ... @@ -20,7 +20,6 @@ add_core_test(${test_prefix}ToS tos.cpp tos_tests_helper.cpp) 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) ... ...
 #include #include #include #include #include #include #include #include #include ... ... @@ -358,4 +358,41 @@ TEST(Morpho, AlphaTree3DImageHQUEUE) ASSERT_IMAGES_EQ_EXP(cut(t, nm, 0u), ref_0); ASSERT_IMAGES_EQ_EXP(cut(t, nm, 16u), ref_16); } TEST(Morpho, AlphaTreeCutMeanLabelization) { mln::image2d ima = { {1, 1, 5}, // {2, 5, 6}, // {0, 1, 4} // }; mln::image2d cut_1 = { {1, 1, 5}, // {1, 5, 5}, // {0, 0, 4} // }; mln::image2d cut_2 = { {1, 1, 5}, // {1, 5, 5}, // {1, 1, 5} // }; mln::image2d cut_3 = { {2, 2, 2}, // {2, 2, 2}, // {2, 2, 2} // }; auto [t, nm] = mln::morpho::alphatree(ima, mln::c4); auto val = t.compute_attribute_on_values(nm, ima, mln::accu::accumulators::mean()); auto make_cut = [&t, &nm, &val](const typename decltype(t.values)::value_type threshold) { auto lbl = t.horizontal_cut(threshold, nm); return t.reconstruct_from(lbl, ::ranges::make_span(val)); }; ASSERT_IMAGES_EQ_EXP(make_cut(0), ima); ASSERT_IMAGES_EQ_EXP(make_cut(1), cut_1); ASSERT_IMAGES_EQ_EXP(make_cut(2), cut_2); ASSERT_IMAGES_EQ_EXP(make_cut(3), cut_3); } \ No newline at end of file
 #include #include #include #include #include #include #include #include TEST(Morpho, AlphaTreeCutMeanLabelization) { mln::image2d ima = { {1, 1, 5}, // {2, 5, 6}, // {0, 1, 4} // }; mln::image2d cut_1 = { {1, 1, 5}, // {1, 5, 5}, // {0, 0, 4} // }; mln::image2d cut_2 = { {1, 1, 5}, // {1, 5, 5}, // {1, 1, 5} // }; mln::image2d cut_3 = { {2, 2, 2}, // {2, 2, 2}, // {2, 2, 2} // }; auto [t, nm] = mln::morpho::alphatree(ima, mln::c4); auto val = t.compute_attribute_on_values(nm, ima, mln::accu::accumulators::mean()); ASSERT_IMAGES_EQ_EXP(mln::morpho::horizontal_cut_labelization_from(t, nm, 0, val), ima); 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
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