Commit 30989c68 authored by Victor Simonin's avatar Victor Simonin
Browse files

Merge branch 'development/watershed-hierarchy-saliency-integration' of...

Merge branch 'development/watershed-hierarchy-saliency-integration' of gitlab.lrde.epita.fr:olena/pylene into development/watershed-hierarchy-saliency-integration
parents 06176d91 1797bfcd
Pipeline #28727 passed with stage
in 36 minutes and 39 seconds
......@@ -8,11 +8,10 @@
using image_t = mln::image2d<mln::rgb8>;
static const std::vector<std::string> bench_images = {"plane.png", "nature.png", "mountains.png", "hong_kong.png"};
static const std::vector<std::string> bench_images = {"Aerial_view_of_Olbia.jpg", "Space1_20MB.jpg"};
static const auto alphatree_vect_function = [](const image_t& input) {
mln::morpho::alphatree(input, mln::c4,
[](const auto& a, const auto& b) -> std::double_t { return mln::l2dist(a, b); });
mln::morpho::alphatree(input, mln::c4, [](const auto& a, const auto& b) -> double { return mln::l2dist(a, b); });
};
static const auto alphatree_hq_function = [](const image_t& input) {
......@@ -43,44 +42,24 @@ public:
};
BENCHMARK_F(BMAlphaTree, AlphatreePlaneVect)(benchmark::State& st)
BENCHMARK_F(BMAlphaTree, AlphatreeOlbiaVect)(benchmark::State& st)
{
this->run(st, alphatree_vect_function, 0);
}
BENCHMARK_F(BMAlphaTree, AlphatreePlaneHQ)(benchmark::State& st)
BENCHMARK_F(BMAlphaTree, AlphatreeOlbiaHQ)(benchmark::State& st)
{
this->run(st, alphatree_hq_function, 0);
}
BENCHMARK_F(BMAlphaTree, AlphatreeNatureVect)(benchmark::State& st)
BENCHMARK_F(BMAlphaTree, AlphatreeSpaceVect)(benchmark::State& st)
{
this->run(st, alphatree_vect_function, 1);
}
BENCHMARK_F(BMAlphaTree, AlphatreeNatureHQ)(benchmark::State& st)
BENCHMARK_F(BMAlphaTree, AlphatreeSpaceHQ)(benchmark::State& st)
{
this->run(st, alphatree_hq_function, 1);
}
BENCHMARK_F(BMAlphaTree, AlphatreeMountainsVect)(benchmark::State& st)
{
this->run(st, alphatree_vect_function, 2);
}
BENCHMARK_F(BMAlphaTree, AlphatreeMountainsHQ)(benchmark::State& st)
{
this->run(st, alphatree_hq_function, 2);
}
BENCHMARK_F(BMAlphaTree, AlphatreeHongKongVect)(benchmark::State& st)
{
this->run(st, alphatree_vect_function, 3);
}
BENCHMARK_F(BMAlphaTree, AlphatreeHongKongHQ)(benchmark::State& st)
{
this->run(st, alphatree_hq_function, 3);
}
BENCHMARK_MAIN();
\ No newline at end of file
#include <mln/core/image/view/cast.hpp>
#include <mln/io/imread.hpp>
#include <mln/io/imsave.hpp>
#include <benchmark/benchmark.h>
#include <mln/accu/accumulators/count.hpp>
#include <mln/accu/accumulators/mean.hpp>
#include <mln/core/colors.hpp>
#include <mln/core/image/ndimage.hpp>
#include <mln/core/neighborhood/c4.hpp>
#include <mln/io/imread.hpp>
#include <mln/morpho/watershed_hierarchy.hpp>
using image_t = mln::image2d<mln::rgb8>;
static const std::vector<std::string> bench_images = {"plane.png", "nature.png", "mountains.png", "hong_kong.png"};
static const std::vector<std::string> bench_images = {"Aerial_view_of_Olbia.jpg", "Space1_20MB.jpg"};
static const auto watershed_hierarchy_by_area_function = [](const image_t& input) {
mln::morpho::watershed_hierarchy(input, mln::accu::features::count<>(), mln::c4,
[](const auto& a, const auto& b) -> std::float_t { return mln::l2dist(a, b); });
mln::morpho::watershed_hierarchy(
input,
[](auto tree, auto nm) -> std::vector<size_t> {
return tree.compute_attribute_on_points(nm, mln::accu::features::count<>());
},
mln::c4, [](const auto& a, const auto& b) -> std::float_t { return mln::l2dist(a, b); });
};
static const auto hierarchical_segmentation_function = [](const image_t& input) {
auto [tree, node_map] =
mln::morpho::watershed_hierarchy(input, mln::accu::features::count<>(), mln::c4,
[](const auto& a, const auto& b) -> std::float_t { return mln::l2dist(a, b); });
auto [tree, node_map] = mln::morpho::watershed_hierarchy(
input,
[](auto tree, auto nm) -> std::vector<size_t> {
return tree.compute_attribute_on_points(nm, mln::accu::features::count<>());
},
mln::c4, [](const auto& a, const auto& b) -> std::float_t { return mln::l2dist(a, b); });
auto mean = tree.compute_attribute_on_values(node_map, input, mln::accu::accumulators::mean<mln::rgb8>());
......@@ -50,49 +53,29 @@ public:
callback(inputs[i]);
}
std::vector<image_t> inputs;
static std::vector<image_t> inputs;
};
std::vector<image_t> BMWatershedHierarchy::inputs;
BENCHMARK_F(BMWatershedHierarchy, WatershedHierarchyAreaPlane)(benchmark::State& st)
BENCHMARK_F(BMWatershedHierarchy, WatershedHierarchyAreaOlbia)(benchmark::State& st)
{
this->run(st, watershed_hierarchy_by_area_function, 0);
}
BENCHMARK_F(BMWatershedHierarchy, HierarchicalSegmentationPlane)(benchmark::State& st)
BENCHMARK_F(BMWatershedHierarchy, HierarchicalSegmentationOlbia)(benchmark::State& st)
{
this->run(st, hierarchical_segmentation_function, 0);
}
BENCHMARK_F(BMWatershedHierarchy, WatershedHierarchyAreaNature)(benchmark::State& st)
BENCHMARK_F(BMWatershedHierarchy, WatershedHierarchyAreaSpace)(benchmark::State& st)
{
this->run(st, watershed_hierarchy_by_area_function, 1);
}
BENCHMARK_F(BMWatershedHierarchy, HierarchicalSegmentationNature)(benchmark::State& st)
BENCHMARK_F(BMWatershedHierarchy, HierarchicalSegmentationSpace)(benchmark::State& st)
{
this->run(st, hierarchical_segmentation_function, 1);
}
BENCHMARK_F(BMWatershedHierarchy, WatershedHierarchyAreaMountains)(benchmark::State& st)
{
this->run(st, watershed_hierarchy_by_area_function, 2);
}
BENCHMARK_F(BMWatershedHierarchy, HierarchicalSegmentationMountains)(benchmark::State& st)
{
this->run(st, hierarchical_segmentation_function, 2);
}
BENCHMARK_F(BMWatershedHierarchy, WatershedHierarchyAreaHongKong)(benchmark::State& st)
{
this->run(st, watershed_hierarchy_by_area_function, 3);
}
BENCHMARK_F(BMWatershedHierarchy, HierarchicalSegmentationHongKong)(benchmark::State& st)
{
this->run(st, hierarchical_segmentation_function, 3);
}
BENCHMARK_MAIN();
\ No newline at end of file
......@@ -64,5 +64,7 @@ add_benchmark(BMMorphers BMMorphers.cpp BMMorphers_main.cpp)
add_benchmark(BMReference_Linear BMReference_Linear.cpp BMReference_Linear_Reversed.cpp BMReference_Linear_main.cpp)
add_benchmark(BMReference_Neighborhood BMReference_Neighborhood_main.cpp)
add_benchmark(BMBufferPrimitives BMBufferPrimitives.cpp)
add_benchmark(BMAlphaTree BMAlphaTree.cpp)
add_benchmark(BMWatershedHierarchy BMWatershedHierarchy.cpp)
ExternalData_Add_Target(fetch-external-data)
......@@ -6,14 +6,14 @@ Hierarchical Watershed
.. cpp:namespace:: mln::morpho
.. cpp:function:: auto watershed_hierarchy(Image f, Accumulator acc, Neighborhood nbh, F dist);
.. cpp:function:: auto watershed_hierarchy(Image f, AttributeFunction attribute_func, Neighborhood nbh, F dist);
Compute the watershed hierarchy and returns a pair `(tree, node_map)`.
See :doc:`component_tree` for more information about the representation of tree.
The implementation is based on [Naj13]_.
:param input: The input image
:param acc: The accumulator that define the attribute computation
:param attribute_func: The function that define the attribute computation
:param nbh: The neighborhood relation
:param dist: The function weighting the edges between two pixels.
:return: A pair `(tree, node_map)` where *tree* is of type ``component_tree<std::invoke_result_t<F, image_value_t<Image>, image_value_t<Image>>>`` and *node_map* is a mapping between the image pixels and the node of the tree.
......@@ -75,8 +75,10 @@ This example is used to generate the grayscale lena watershed hierarchy by area
mln::image2d<uint8_t> input = ...;
// Compute the watershed hierarchy by area
auto area_acc = mln::accu::features::count<>();
auto [tree, node_map] = mln::morpho::watershed_hierarchy(input, area_acc, mln::c4);
auto area_attribute_func = [](auto tree, auto node_map) -> std::vector<size_t> {
return tree.compute_attribute_on_points(node_map, mln::accu::features::count<>());
};
auto [tree, node_map] = mln::morpho::watershed_hierarchy(input, area_attribute_func, mln::c4);
// Compute an attribute (for example the average pixels value at each node, as below)
auto mean = tree.compute_attribute_on_values(node_map, input, mln::accu::accumulators::mean<uint8_t>());
......
......@@ -6,20 +6,20 @@ set(PYLENE_IMAGE_DIR ${PROJECT_SOURCE_DIR}/img)
set(DOCUMENTATION_IMAGES "")
function(add_image EXECUTABLE INPUT)
# add_image(EXECUTABLE INPUT OUTPUT1 [ [OUTPUT2 [OUTPUT3 ...] ] ]
set(outputs "")
foreach (output IN LISTS ARGN)
set(output "${DOCUMENTATION_IMAGE_DIR}/${output}")
list(APPEND outputs "${output}")
list(APPEND DOCUMENTATION_IMAGES "${output}")
endforeach ()
set(DOCUMENTATION_IMAGES ${DOCUMENTATION_IMAGES} PARENT_SCOPE)
add_custom_command(OUTPUT ${outputs}
COMMAND ${EXECUTABLE} ${INPUT} ${outputs}
COMMAND_EXPAND_LISTS
)
# add_image(EXECUTABLE INPUT OUTPUT1 [ [OUTPUT2 [OUTPUT3 ...] ] ]
set(outputs "")
foreach (output IN LISTS ARGN)
set(output "${DOCUMENTATION_IMAGE_DIR}/${output}")
list(APPEND outputs "${output}")
list(APPEND DOCUMENTATION_IMAGES "${output}")
endforeach ()
set(DOCUMENTATION_IMAGES ${DOCUMENTATION_IMAGES} PARENT_SCOPE)
add_custom_command(OUTPUT ${outputs}
COMMAND ${EXECUTABLE} ${INPUT} ${outputs}
COMMAND_EXPAND_LISTS
)
endfunction()
add_image("intro-1" "${DOCUMENTATION_IMAGE_DIR}/Olena-c6gradi.png" intro-1-1.png)
......@@ -40,18 +40,18 @@ add_image("cdt;4" "${DOCUMENTATION_IMAGE_DIR}/F.png" F-5-7-11.png)
add_image("staff_lines" "${DOCUMENTATION_IMAGE_DIR}/staff_lines.pbm"
morpho_hitormiss_1.png morpho_hitormiss_2.png staff_lines_markers.png morpho_reconstruction_1.png morpho_reconstruction_2.png)
morpho_hitormiss_1.png morpho_hitormiss_2.png staff_lines_markers.png morpho_reconstruction_1.png morpho_reconstruction_2.png)
add_image("reconstruction"
"${DOCUMENTATION_IMAGE_DIR}/blobs2_binary.png"
morpho_reconstruction_dilated.png
morpho_reconstruction_markers.png
morpho_reconstruction_rec.png
morpho_reconstruction_out.png)
"${DOCUMENTATION_IMAGE_DIR}/blobs2_binary.png"
morpho_reconstruction_dilated.png
morpho_reconstruction_markers.png
morpho_reconstruction_rec.png
morpho_reconstruction_out.png)
add_image("area_filter"
"${DOCUMENTATION_IMAGE_DIR}/blobs2_binary.png"
morpho_area_filter_dilated.png
morpho_area_filter_opening.png
morpho_area_filter_out.png)
"${DOCUMENTATION_IMAGE_DIR}/blobs2_binary.png"
morpho_area_filter_dilated.png
morpho_area_filter_opening.png
morpho_area_filter_out.png)
add_image("blobs_watershed" "${DOCUMENTATION_IMAGE_DIR}/blobs_binary.png" blobs_distance_transform.png blobs_segmentation.png)
......@@ -63,7 +63,7 @@ add_image("watershed_hierarchy_example" "${PYLENE_IMAGE_DIR}/lena.ppm" watershed
add_image("watershed_hierarchy_example" "${PYLENE_IMAGE_DIR}/lena.pgm" watershed_hierarchy_area_gray.png)
add_custom_target(build-images
DEPENDS "${DOCUMENTATION_IMAGES}")
DEPENDS "${DOCUMENTATION_IMAGES}")
add_library(doc-lib lut.cpp)
target_link_libraries(doc-lib Pylene::Pylene)
......@@ -88,7 +88,7 @@ target_compile_definitions(erosion-cli PRIVATE BOOST_ALL_NO_LIB)
# for program_options, need to separate CONAN and regular FindBoost
if (TARGET Boost::program_options)
target_link_libraries(erosion-cli PRIVATE Boost::program_options)
target_link_libraries(erosion-cli PRIVATE Boost::program_options)
elseif (TARGET Boost::Boost)
target_link_libraries(erosion-cli PRIVATE Boost::Boost)
endif ()
\ No newline at end of file
target_link_libraries(erosion-cli PRIVATE Boost::Boost)
endif()
\ No newline at end of file
......@@ -13,7 +13,10 @@ template <typename V>
void process_example(const mln::image2d<V>& img, const std::string& output_filename, const double threshold)
{
// 2. Build the watershed hierarchy
auto [t, nm] = mln::morpho::watershed_hierarchy(img, mln::accu::features::count<>(), mln::c4);
auto area_attribute_func = [](auto tree, auto nm) -> std::vector<size_t> {
return tree.compute_attribute_on_points(nm, mln::accu::features::count<>());
};
auto [t, nm] = mln::morpho::watershed_hierarchy(img, area_attribute_func, mln::c4);
// 3. Compute the mean attribute
auto mean = t.compute_attribute_on_values(nm, img, mln::accu::accumulators::mean<V>());
......
......@@ -316,7 +316,7 @@ namespace mln::morpho
return {std::move(t), std::move(node_map)};
}
template <class I, class N, class F, bool HQ = true,
template <bool HQ = true, 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, bool canonize_tree = true, bool compute_flatzones = true,
......
......@@ -5,17 +5,18 @@
namespace mln::morpho
{
// FIXME Pass a std::function instead of an accumulator ?
/// Compute the watershed hierarchy of an image
///
/// \param input The input image
/// \param acc The accumulator that define the attribute computation
/// \param attribute_func The function that define the attribute computation
/// \param neighborhood The neighborhood relation
/// \param distance Distance function
template <class I, class Accu, class N, class F = mln::functional::l2dist_t<>>
std::pair<component_tree<typename accu::result_of<Accu, image_point_t<I>>::type>, image_ch_value_t<I, int>> //
watershed_hierarchy(I input, Accu acc, N nbh, F distance = F{});
template <class I, class A, class N, class F = mln::functional::l2dist_t<>>
std::pair<component_tree<typename std::invoke_result_t<
A, component_tree<std::invoke_result_t<F, image_value_t<I>, image_value_t<I>>>,
image_ch_value_t<I, int>>::value_type>,
image_ch_value_t<I, int>> //
watershed_hierarchy(I input, A attribute_func, N nbh, F distance = F{});
/******************************************/
......@@ -77,14 +78,121 @@ namespace mln::morpho
}
} // namespace internal
template <class I, class Accu, class N, class F>
std::pair<component_tree<typename accu::result_of<Accu, image_point_t<I>>::type>, image_ch_value_t<I, int>> //
watershed_hierarchy(I input, Accu acc, N nbh, F distance)
template <typename W, class I>
std::vector<W> height_attribute(component_tree<W> tree, I node_map)
{
int n = static_cast<int>(tree.parent.size());
int nb_leaves = static_cast<int>(node_map.domain().size());
std::vector<W> deepest_altitude(n, std::numeric_limits<W>::max());
for (int i = n - 1; i >= 0; --i)
{
int parent = tree.parent[i];
if (i > (n - nb_leaves - 1))
deepest_altitude[i] = tree.values[parent];
deepest_altitude[parent] = std::min(deepest_altitude[parent], deepest_altitude[i]);
}
std::vector<W> height(n);
for (int i = n - 1; i >= 0; --i)
height[i] = tree.values[tree.parent[i]] - deepest_altitude[i];
return height;
}
template <typename W, class I>
std::vector<bool> extrema_attribute(component_tree<W> tree, I node_map)
{
int n = static_cast<int>(tree.parent.size());
int nb_leaves = static_cast<int>(node_map.domain().size());
std::vector<bool> extrema(n, true);
std::fill_n(extrema.end() - nb_leaves, nb_leaves, false);
for (int i = (n - nb_leaves - 1); i > 0; --i)
{
int parent = tree.parent[i];
bool same_weight = tree.values[i] == tree.values[parent];
extrema[parent] = extrema[parent] && same_weight && extrema[i];
extrema[i] = extrema[i] && !same_weight;
}
return extrema;
}
template <typename W, class I>
std::vector<W> dynamic_attribute(component_tree<W> tree, I node_map)
{
int n = static_cast<int>(tree.parent.size());
int nb_leaves = static_cast<int>(node_map.domain().size());
std::vector<W> deepest_altitude(n, std::numeric_limits<W>::max());
std::vector<int> path_to_minima(n, -1);
// Compute deepest altitude and path to deepest minima
for (int i = (n - nb_leaves - 1); i >= 0; --i)
{
int parent = tree.parent[i];
// Deepest non leaf node
if (deepest_altitude[i] == std::numeric_limits<W>::max())
deepest_altitude[i] = tree.values[i];
if (deepest_altitude[i] < deepest_altitude[parent])
{
deepest_altitude[parent] = deepest_altitude[i];
path_to_minima[parent] = i;
}
}
auto extrema = extrema_attribute(tree, node_map);
std::vector<int> nearest_minima(n, -1);
std::vector<W> dynamic(n);
dynamic[0] = tree.values[0] - deepest_altitude[0];
for (int i = 1; i < n; ++i)
{
int parent = tree.parent[i];
if (i > (n - nb_leaves - 1))
{
if (nearest_minima[parent] != -1)
dynamic[i] = dynamic[nearest_minima[parent]];
else
dynamic[i] = 0;
continue;
}
if (extrema[i])
nearest_minima[i] = i;
else
nearest_minima[i] = nearest_minima[parent];
if (i == path_to_minima[parent])
dynamic[i] = dynamic[parent];
else
dynamic[i] = tree.values[parent] - deepest_altitude[i];
}
return dynamic;
}
template <class I, class A, class N, class F>
std::pair<component_tree<typename std::invoke_result_t<
A, component_tree<std::invoke_result_t<F, image_value_t<I>, image_value_t<I>>>,
image_ch_value_t<I, int>>::value_type>,
image_ch_value_t<I, int>> //
watershed_hierarchy(I input, A attribute_func, N nbh, F distance)
{
std::vector<internal::edge_t<image_point_t<I>, std::invoke_result_t<F, image_value_t<I>, image_value_t<I>>>> mst;
auto [tree, nm] = internal::__alphatree<I, N, F, false>(input, nbh, distance, false, false, &mst);
auto [tree, nm] = internal::__alphatree<false>(input, nbh, distance, false, false, &mst);
auto attribute = tree.compute_attribute_on_points(nm, acc);
auto attribute = attribute_func(tree, nm);
auto node_count = tree.parent.size();
mln::for_each(nm, [node_count](int& id) { id = static_cast<int>(node_count) - id - 1; });
......
......@@ -8,6 +8,75 @@
#include <gtest/gtest.h>
TEST(Morpho, HeightAttribute)
{
mln::image2d<uint8_t> input = {
{163, 112, 42, 121, 112}, //
{42, 121, 1, 42, 255}, //
{1, 112, 121, 112, 121}, //
{112, 255, 42, 1, 42}, //
{121, 112, 121, 112, 163}, //
};
std::vector<float> expected_attribute = {
134.f, 134.f, 125.f, 70.f, 70.f, 70.f, 70.f, 70.f, 61.f, 38.f, 0.f, 29.f, 38.f, 0.f, 42.f, 0.f, 0.f,
0.f, 42.f, 0.f, 0.f, 0.f, 70.f, 0.f, 0.f, 0.f, 0.f, 0.f, 0.f, 0.f, 0.f, 0.f, 0.f, 0.f,
0.f, 0.f, 0.f, 0.f, 0.f, 0.f, 0.f, 0.f, 0.f, 0.f, 0.f, 0.f, 0.f, 0.f, 0.f};
// Build BPT
auto [tree, nm] = mln::morpho::internal::__alphatree<false>(
input, mln::c4, [](const auto& a, const auto& b) -> float { return mln::functional::l2dist_t<>()(a, b); }, false,
false);
auto attribute = mln::morpho::height_attribute(tree, nm);
ASSERT_EQ(expected_attribute.size(), attribute.size());
for (std::size_t i = 0; i < expected_attribute.size(); ++i)
EXPECT_EQ(expected_attribute[i], attribute[i]);
}
TEST(Morpho, DynamicAttribute)
{
mln::image2d<uint8_t> input = {
{163, 112, 42, 121, 112}, //
{42, 121, 1, 42, 255}, //
{1, 112, 121, 112, 121}, //
{112, 255, 42, 1, 42}, //
{121, 112, 121, 112, 163}, //
};
// Build BPT
auto [tree, nm] = mln::morpho::internal::__alphatree<false>(
input, mln::c4, [](const auto& a, const auto& b) -> float { return mln::functional::l2dist_t<>()(a, b); }, false,
false);
std::vector<bool> expected_extrema_attribute = {false, false, false, false, false, false, false, false, false, true,
false, true, true, false, true, false, false, false, true, false,
false, false, true, false, false, false, false, false, false, false,
false, false, false, false, false, false, false, false, false, false,
false, false, false, false, false, false, false, false, false};
auto extrema_attribute = mln::morpho::extrema_attribute(tree, nm);
ASSERT_EQ(expected_extrema_attribute.size(), extrema_attribute.size());
for (std::size_t i = 0; i < expected_extrema_attribute.size(); ++i)
EXPECT_EQ(expected_extrema_attribute[i], extrema_attribute[i]);
std::vector<float> expected_dynamic_attribute = {
134.f, 134.f, 134.f, 70.f, 70.f, 70.f, 70.f, 134.f, 70.f, 38.f, 38.f, 29.f, 38.f, 29.f, 134.f, 134.f, 134.f,
134.f, 70.f, 70.f, 70.f, 70.f, 70.f, 70.f, 0, 134.f, 134.f, 134.f, 134.f, 38.f, 38.f, 38.f, 0, 134.f,
70.f, 70.f, 70.f, 70.f, 38.f, 0, 29.f, 29.f, 70.f, 38.f, 70.f, 70.f, 29.f, 70.f, 0};
auto dynamic_attribute = mln::morpho::dynamic_attribute(tree, nm);
ASSERT_EQ(expected_dynamic_attribute.size(), dynamic_attribute.size());
for (std::size_t i = 0; i < expected_dynamic_attribute.size(); ++i)
EXPECT_EQ(expected_dynamic_attribute[i], dynamic_attribute[i]);
}
TEST(Morpho, AreaWatershedHierarchyGray)
{
mln::image2d<uint8_t> input = {
......@@ -25,8 +94,11 @@ TEST(Morpho, AreaWatershedHierarchyGray)
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0};
auto [tree, _] = mln::morpho::watershed_hierarchy(
input, mln::accu::features::count<>(), mln::c4,
[](const auto& a, const auto& b) -> float { return mln::functional::l2dist_t<>()(a, b); });
input,
[](auto tree, auto nm) -> std::vector<size_t> {
return tree.compute_attribute_on_points(nm, mln::accu::features::count<>());
},
mln::c4, [](const auto& a, const auto& b) -> float { return mln::functional::l2dist_t<>()(a, b); });
ASSERT_EQ(expected_parent.size(), tree.parent.size());
ASSERT_EQ(expected_values.size(), tree.values.size());
......@@ -56,8 +128,11 @@ TEST(Morpho, AreaWatershedHierarchyGrayHQ)
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0};
auto [tree, _] = mln::morpho::watershed_hierarchy(
input, mln::accu::features::count<>(), mln::c4,
[](const auto& a, const auto& b) -> std::uint8_t { return mln::functional::l2dist_t<>()(a, b); });
input,
[](auto tree, auto nm) -> std::vector<size_t> {
return tree.compute_attribute_on_points(nm, mln::accu::features::count<>());
},
mln::c4, [](const auto& a, const auto& b) -> std::uint8_t { return mln::functional::l2dist_t<>()(a, b); });
ASSERT_EQ(expected_parent.size(), tree.parent.size());
ASSERT_EQ(expected_values.size(), tree.values.size());
......@@ -87,8 +162,11 @@ TEST(Morpho, AreaWatershedHierarchyRGB)
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0};
auto [tree, _] = mln::morpho::watershed_hierarchy(
input, mln::accu::features::count<>(), mln::c4,
[](const auto& a, const auto& b) -> float { return mln::functional::l2dist_t<>()(a, b); });
input,
[](auto tree, auto nm) -> std::vector<size_t> {
return tree.compute_attribute_on_points(nm, mln::accu::features::count<>());
},
mln::c4, [](const auto& a, const auto& b) -> float { return mln::functional::l2dist_t<>()(a, b); });
ASSERT_EQ(expected_parent.size(), tree.parent.size());
ASSERT_EQ(expected_values.size(), tree.values.size());
......@@ -118,8 +196,11 @@ TEST(Morpho, AreaWatershedHierarchyGrayC8)
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0};