Commit f52d1a7c authored by Edwin Carlinet's avatar Edwin Carlinet
Browse files

Merge branch 'development/labelization' into 'development/ranges'

Migration of Local Extremum Labelization Algorithm

See merge request !75
parents 8b2196cb 25a92452
Pipeline #13731 failed with stages
in 92 minutes and 45 seconds
......@@ -25,8 +25,7 @@ BraceWrapping:
NamespaceIndentation: All
FixNamespaceComments: true
IndentWrappedFunctionNames: true
# AllowShortFunctionsOnASingleLine: None
IndentWrappedFunctionNames: false # Do not indent after function def break
AllowShortFunctionsOnASingleLine: Inline
AlwaysBreakTemplateDeclarations: true
AlignAfterOpenBracket: Align
......
......@@ -16,6 +16,8 @@
#include <mln/morpho/experimental/opening.hpp>
#include <mln/morpho/experimental/reconstruction.hpp>
#include <mln/labeling/experimental/local_extrema.hpp>
#include <benchmark/benchmark.h>
#include <fixtures/ImagePath/image_path.hpp>
......@@ -203,4 +205,15 @@ BENCHMARK_F(BMMorpho, Hit_or_miss_corner)(benchmark::State& st)
}
BENCHMARK_F(BMMorpho, minima)(benchmark::State& st)
{
auto f = [](const image_t& input, image_t&) {
int nlabel;
mln::labeling::experimental::local_minima<int8_t>(input, mln::experimental::c4, nlabel);
return nlabel;
};
this->run(st, f);
}
BENCHMARK_MAIN();
......@@ -16,6 +16,8 @@
#include <mln/morpho/structural/erode.hpp>
#include <mln/morpho/structural/opening.hpp>
#include <mln/labeling/local_extrema.hpp>
// [legacy]
#include <mln/core/image/image2d.hpp>
......@@ -150,5 +152,15 @@ BENCHMARK_F(BMMorpho, Hit_or_miss_corner)(benchmark::State& st)
this->run(st, f);
}
BENCHMARK_F(BMMorpho, minima)(benchmark::State& st)
{
auto f = [](const image_t& input, image_t&) {
int nlabel;
mln::labeling::local_minima<int8_t>(input, mln::c4, nlabel);
return nlabel;
};
this->run(st, f);
}
BENCHMARK_MAIN();
......@@ -25,10 +25,10 @@ ExternalData_Expand_Arguments(
if (CMAKE_CXX_COMPILER_ID STREQUAL "Clang")
set(STANDALONE_COMPILE_FLAGS "-Rpass=loop-vectorize -Rpass-missed=loop-vectorize -Rpass-analysis=loop-vectorize -gline-tables-only -gcolumn-info ")
set(CMAKE_CXX_FLAGS_RELEASE "${CMAKE_CXX_FLAGS_RELEASE} -O3 -march=native")
set(CMAKE_CXX_FLAGS_RELEASE "${CMAKE_CXX_FLAGS_RELEASE} -O3 -march=native -g")
elseif (CMAKE_CXX_COMPILER_ID STREQUAL "GNU")
set(STANDALONE_COMPILE_FLAGS "-g -fopt-info -fopt-info-vec-missed -ftree-vectorize -ftree-vectorizer-verbose=2")
set(CMAKE_CXX_FLAGS_RELEASE "${CMAKE_CXX_FLAGS_RELEASE} -O3 -march=native")
set(STANDALONE_COMPILE_FLAGS "-g -fopt-info-vec -fopt-info-vec-missed -ftree-vectorize -ftree-vectorizer-verbose=2")
set(CMAKE_CXX_FLAGS_RELEASE "${CMAKE_CXX_FLAGS_RELEASE} -O3 -march=native -g")
else ()
# W4141: 'inline' : utilisé plusieurs fois in benchmark.h
set(STANDALONE_COMPILE_FLAGS " ")
......
......@@ -203,7 +203,7 @@ namespace mln
/******************************************/
template <class T, int N>
experimental::ndbox<N> __ndbuffer_image<T, N>::domain() const noexcept
inline experimental::ndbox<N> __ndbuffer_image<T, N>::domain() const noexcept
{
experimental::ndpoint<N> pmin, pmax;
for (int k = 0; k < N; ++k)
......@@ -216,20 +216,21 @@ namespace mln
template <class T, int N>
void __ndbuffer_image<T, N>::__init(alloc_fun_t __allocate, int topleft[], int sizes[], std::ptrdiff_t byte_strides[],
inline void __ndbuffer_image<T, N>::__init(alloc_fun_t __allocate, int topleft[], int sizes[], std::ptrdiff_t byte_strides[],
const image_build_params& params)
{
base::__init(__allocate, sample_type_traits<T>::id(), sizeof(T), N, topleft, sizes, byte_strides, params);
}
template <class T, int N>
auto __ndbuffer_image<T, N>::concretize() const noexcept -> image_builder<concrete_type, __ndbuffer_image<T,N>>
inline auto __ndbuffer_image<T, N>::concretize() const noexcept -> image_builder<concrete_type, __ndbuffer_image<T,N>>
{
return {*this};
}
template <class T, int N>
template <class V>
inline
auto __ndbuffer_image<T, N>::ch_value() const noexcept -> image_builder<ch_value_type<V>, __ndbuffer_image<T, N>>
{
return {*this};
......@@ -237,7 +238,7 @@ namespace mln
template <class T, int N>
__ndbuffer_image<T, N> __ndbuffer_image<T, N>::from_buffer(T* buffer, int topleft[], int sizes[],
inline __ndbuffer_image<T, N> __ndbuffer_image<T, N>::from_buffer(T* buffer, int topleft[], int sizes[],
std::ptrdiff_t byte_strides[], bool copy)
{
image_build_params params;
......@@ -264,7 +265,7 @@ namespace mln
}
template <class T, int N>
__ndbuffer_image<T, N> __ndbuffer_image<T, N>::from_buffer(T* buffer, int sizes[], std::ptrdiff_t byte_strides[], bool copy)
inline __ndbuffer_image<T, N> __ndbuffer_image<T, N>::from_buffer(T* buffer, int sizes[], std::ptrdiff_t byte_strides[], bool copy)
{
int topleft[N] = {0};
return __ndbuffer_image<T, N>::from_buffer(buffer, topleft, sizes, byte_strides, copy);
......@@ -272,7 +273,7 @@ namespace mln
template <class T, int N>
template <class E>
__ndbuffer_image<T, N> __ndbuffer_image<T, N>::from(const mln::ndimage_base<T, N, E>& other)
inline __ndbuffer_image<T, N> __ndbuffer_image<T, N>::from(const mln::ndimage_base<T, N, E>& other)
{
auto tmp = base::from(other);
return static_cast<__ndbuffer_image&>(tmp);
......@@ -280,20 +281,20 @@ namespace mln
template <class T, int N>
__ndbuffer_image<T,N>::__ndbuffer_image(experimental::ndbox<N> domain, const image_build_params& params)
inline __ndbuffer_image<T,N>::__ndbuffer_image(experimental::ndbox<N> domain, const image_build_params& params)
{
this->resize(domain, params);
}
template <class T, int N>
__ndbuffer_image<T, N>::__ndbuffer_image()
inline __ndbuffer_image<T, N>::__ndbuffer_image()
: base(details::ndbuffer_image_info_t{sample_type_traits<T>::id(), N, {}, nullptr})
{
}
template <class T, int N>
template <int, class>
__ndbuffer_image<T, N>::__ndbuffer_image(int width, const image_build_params& params)
inline __ndbuffer_image<T, N>::__ndbuffer_image(int width, const image_build_params& params)
{
this->resize(width, params);
}
......@@ -301,14 +302,14 @@ namespace mln
template <class T, int N>
template <int, class>
__ndbuffer_image<T, N>::__ndbuffer_image(int width, int height, const image_build_params& params)
inline __ndbuffer_image<T, N>::__ndbuffer_image(int width, int height, const image_build_params& params)
{
this->resize(width, height, params);
}
template <class T, int N>
template <int, class>
__ndbuffer_image<T, N>::__ndbuffer_image(int width, int height, int depth, const image_build_params& params)
inline __ndbuffer_image<T, N>::__ndbuffer_image(int width, int height, int depth, const image_build_params& params)
{
this->resize(width, height, depth, params);
}
......@@ -316,21 +317,21 @@ namespace mln
template <class T, int N>
template <int M, class>
__ndbuffer_image<T, N>::__ndbuffer_image(std::initializer_list<T> values)
inline __ndbuffer_image<T, N>::__ndbuffer_image(std::initializer_list<T> values)
: base(std::move(values))
{
}
template <class T, int N>
template <int M, class>
__ndbuffer_image<T, N>::__ndbuffer_image(std::initializer_list<std::initializer_list<T>> values)
inline __ndbuffer_image<T, N>::__ndbuffer_image(std::initializer_list<std::initializer_list<T>> values)
: base(std::move(values))
{
}
template <class T, int N>
template <int M, class>
__ndbuffer_image<T, N>::__ndbuffer_image(
inline __ndbuffer_image<T, N>::__ndbuffer_image(
std::initializer_list<std::initializer_list<std::initializer_list<T>>> values)
: base(std::move(values))
{
......@@ -339,13 +340,13 @@ namespace mln
template <class T, int N>
template <class U>
__ndbuffer_image<T, N>::__ndbuffer_image(const __ndbuffer_image<U, N>& other, const image_build_params& params)
inline __ndbuffer_image<T, N>::__ndbuffer_image(const __ndbuffer_image<U, N>& other, const image_build_params& params)
{
this->resize(other, params);
}
template <class T, int N>
void __ndbuffer_image<T, N>::resize(experimental::ndbox<N> domain, const image_build_params& params)
inline void __ndbuffer_image<T, N>::resize(experimental::ndbox<N> domain, const image_build_params& params)
{
int tl[N];
int sz[N];
......@@ -362,7 +363,7 @@ namespace mln
template <class T, int N>
template <int, class>
void __ndbuffer_image<T, N>::resize(int width, const image_build_params& params)
inline void __ndbuffer_image<T, N>::resize(int width, const image_build_params& params)
{
auto domain = experimental::ndbox<N>(width);
resize(domain, params);
......@@ -371,7 +372,7 @@ namespace mln
template <class T, int N>
template <int, class>
void __ndbuffer_image<T, N>::resize(int width, int height, const image_build_params& params)
inline void __ndbuffer_image<T, N>::resize(int width, int height, const image_build_params& params)
{
auto domain = experimental::ndbox<N>(width, height);
resize(domain, params);
......@@ -380,7 +381,7 @@ namespace mln
template <class T, int N>
template <int, class>
void __ndbuffer_image<T, N>::resize(int width, int height, int depth, const image_build_params& params)
inline void __ndbuffer_image<T, N>::resize(int width, int height, int depth, const image_build_params& params)
{
auto domain = experimental::ndbox<N>(width, height, depth);
resize(domain, params);
......@@ -389,7 +390,7 @@ namespace mln
template <class T, int N>
template <class U>
void __ndbuffer_image<T, N>::resize(const __ndbuffer_image<U, N>& other, image_build_params params)
inline void __ndbuffer_image<T, N>::resize(const __ndbuffer_image<U, N>& other, image_build_params params)
{
if (params.border == -1)
params.border = other.border();
......@@ -400,7 +401,7 @@ namespace mln
template <class T, int N>
auto __ndbuffer_image<T, N>::point_at_index(index_type i) const noexcept -> point_type
inline auto __ndbuffer_image<T, N>::point_at_index(index_type i) const noexcept -> point_type
{
point_type coords;
Impl::get_point(this->__info(), i, coords.data());
......@@ -408,7 +409,7 @@ namespace mln
}
template <class T, int N>
auto __ndbuffer_image<T, N>::index_of_point(point_type p) const noexcept -> index_type
inline auto __ndbuffer_image<T, N>::index_of_point(point_type p) const noexcept -> index_type
{
assert(Impl::is_point_valid(this->__info(), p.data()));
return Impl::get_index(this->__info(), p.data());
......@@ -416,25 +417,25 @@ namespace mln
template <class T, int N>
auto __ndbuffer_image<T, N>::delta_index(point_type p) const noexcept -> index_type
inline auto __ndbuffer_image<T, N>::delta_index(point_type p) const noexcept -> index_type
{
return Impl::get_index(this->__info(), p.data());
}
template <class T, int N>
T* __ndbuffer_image<T, N>::buffer() noexcept
inline T* __ndbuffer_image<T, N>::buffer() noexcept
{
return reinterpret_cast<T*>(this->base::buffer());
}
template <class T, int N>
const T* __ndbuffer_image<T, N>::buffer() const noexcept
inline const T* __ndbuffer_image<T, N>::buffer() const noexcept
{
return reinterpret_cast<T*>(this->base::buffer());
}
template <class T, int N>
__ndbuffer_image<T, 2> __ndbuffer_image<T, N>::slice(int z) const
inline __ndbuffer_image<T, 2> __ndbuffer_image<T, N>::slice(int z) const
{
int begin[N] = {this->__axes(0).domain_begin, this->__axes(1).domain_begin, z};
int end[N] = {this->__axes(0).domain_end, this->__axes(1).domain_end, z + 1};
......@@ -450,7 +451,7 @@ namespace mln
}
template <class T, int N>
__ndbuffer_image<T, 1> __ndbuffer_image<T, N>::row(int y) const
inline __ndbuffer_image<T, 1> __ndbuffer_image<T, N>::row(int y) const
{
int begin[N] = {m_axes[0].domain_begin, y};
int end[N] = {m_axes[0].domain_end, y + 1};
......@@ -466,7 +467,7 @@ namespace mln
}
template <class T, int N>
__ndbuffer_image<T, N> __ndbuffer_image<T, N>::clip(const experimental::ndbox<N>& roi) const
inline __ndbuffer_image<T, N> __ndbuffer_image<T, N>::clip(const experimental::ndbox<N>& roi) const
{
__ndbuffer_image tmp = *this;
Impl::select(&tmp, N, roi.tl().data(), roi.br().data());
......@@ -475,21 +476,21 @@ namespace mln
template <class T, int N>
auto __ndbuffer_image<T, N>::operator()(point_type p) const noexcept -> const_reference
inline auto __ndbuffer_image<T, N>::operator()(point_type p) const noexcept -> const_reference
{
assert(Impl::is_point_in_domain(this->__info(), p.data()));
return *Impl::get_pointer(this->__info(), p.data());
}
template <class T, int N>
auto __ndbuffer_image<T, N>::operator()(point_type p) noexcept -> reference
inline auto __ndbuffer_image<T, N>::operator()(point_type p) noexcept -> reference
{
assert(Impl::is_point_in_domain(this->__info(), p.data()));
return *Impl::get_pointer(this->__info(), p.data());
}
template <class T, int N>
auto __ndbuffer_image<T, N>::__pixel_at(point_type p) const noexcept -> new_const_pixel_type
inline auto __ndbuffer_image<T, N>::__pixel_at(point_type p) const noexcept -> new_const_pixel_type
{
point_type lcoords = p;
lcoords[0] = 0;
......@@ -498,7 +499,7 @@ namespace mln
}
template <class T, int N>
auto __ndbuffer_image<T, N>::__pixel_at(point_type p) noexcept -> new_pixel_type
inline auto __ndbuffer_image<T, N>::__pixel_at(point_type p) noexcept -> new_pixel_type
{
point_type lcoords = p;
lcoords[0] = 0;
......@@ -507,62 +508,62 @@ namespace mln
}
template <class T, int N>
auto __ndbuffer_image<T, N>::new_pixel(point_type p) const noexcept -> new_const_pixel_type
inline auto __ndbuffer_image<T, N>::new_pixel(point_type p) const noexcept -> new_const_pixel_type
{
assert(Impl::is_point_in_domain(this->__info(), p.data()));
return this->__pixel_at(p);
}
template <class T, int N>
auto __ndbuffer_image<T, N>::new_pixel(point_type p) noexcept -> new_pixel_type
inline auto __ndbuffer_image<T, N>::new_pixel(point_type p) noexcept -> new_pixel_type
{
assert(Impl::is_point_in_domain(this->__info(), p.data()));
return this->__pixel_at(p);
}
template <class T, int N>
auto __ndbuffer_image<T, N>::new_pixel_at(point_type p) const noexcept -> new_const_pixel_type
inline auto __ndbuffer_image<T, N>::new_pixel_at(point_type p) const noexcept -> new_const_pixel_type
{
assert(Impl::is_point_valid(this->__info(), p.data()));
return this->__pixel_at(p);
}
template <class T, int N>
auto __ndbuffer_image<T, N>::new_pixel_at(point_type p) noexcept -> new_pixel_type
inline auto __ndbuffer_image<T, N>::new_pixel_at(point_type p) noexcept -> new_pixel_type
{
assert(Impl::is_point_valid(this->__info(), p.data()));
return this->__pixel_at(p);
}
template <class T, int N>
auto __ndbuffer_image<T, N>::at(point_type p) const noexcept -> const_reference
inline auto __ndbuffer_image<T, N>::at(point_type p) const noexcept -> const_reference
{
assert(Impl::is_point_valid(this->__info(), p.data()));
return *Impl::get_pointer(this->__info(), p.data());
}
template <class T, int N>
auto __ndbuffer_image<T, N>::at(point_type p) noexcept -> reference
inline auto __ndbuffer_image<T, N>::at(point_type p) noexcept -> reference
{
assert(Impl::is_point_valid(this->__info(), p.data()));
return *Impl::get_pointer(this->__info(), p.data());
}
template <class T, int N>
auto __ndbuffer_image<T, N>::operator[](index_type i) const noexcept -> const_reference
inline auto __ndbuffer_image<T, N>::operator[](index_type i) const noexcept -> const_reference
{
return *Impl::get_pointer(this->__info(), i);
}
template <class T, int N>
auto __ndbuffer_image<T, N>::operator[](index_type i) noexcept -> reference
inline auto __ndbuffer_image<T, N>::operator[](index_type i) noexcept -> reference
{
return *Impl::get_pointer(this->__info(), i);
}
template <class T, int N>
ranges::multi_span<T, N> __ndbuffer_image<T, N>::new_values() noexcept
inline ranges::multi_span<T, N> __ndbuffer_image<T, N>::new_values() noexcept
{
std::array<std::size_t, N> counts;
std::array<std::ptrdiff_t, N> strides;
......@@ -575,7 +576,7 @@ namespace mln
}
template <class T, int N>
ranges::multi_span<const T, N> __ndbuffer_image<T, N>::new_values() const noexcept
inline ranges::multi_span<const T, N> __ndbuffer_image<T, N>::new_values() const noexcept
{
std::array<std::size_t, N> counts;
std::array<std::ptrdiff_t, N> strides;
......@@ -589,14 +590,14 @@ namespace mln
template <class T, int N>
experimental::details::ndpix_range<T, N> __ndbuffer_image<T, N>::new_pixels() noexcept
inline experimental::details::ndpix_range<T, N> __ndbuffer_image<T, N>::new_pixels() noexcept
{
return {this->__info()};
}
template <class T, int N>
experimental::details::ndpix_range<const T, N> __ndbuffer_image<T, N>::new_pixels() const noexcept
inline experimental::details::ndpix_range<const T, N> __ndbuffer_image<T, N>::new_pixels() const noexcept
{
return {this->__info()};
}
......
......@@ -125,7 +125,6 @@ namespace mln::details
}
template <int pdim>
void* ndbuffer_image_impl_base_2<pdim>::get_pointer(const ndbuffer_image_info_t* ima, const int coordinates[])
{
......@@ -135,14 +134,22 @@ namespace mln::details
return ima->m_buffer + x;
}
// Return a pointer to the loc given by p
template <class T, int pdim>
T* ndbuffer_image_impl<T, pdim>::get_pointer(const ndbuffer_image_info_t* ima, const int coordinates[])
{
return reinterpret_cast<T*>(ndbuffer_image_impl_base_2<pdim>::get_pointer(ima, coordinates));
if constexpr (std::is_same_v<T, void>)
return reinterpret_cast<T*>(ndbuffer_image_impl_base_2<pdim>::get_pointer(ima, coordinates));
else
{
std::ptrdiff_t x = 0;
for (int k = 1; k < get_pdim(ima); ++k)
x += coordinates[k] * ima->m_axes[k].byte_stride;
return reinterpret_cast<T*>(ima->m_buffer + x) + coordinates[0];
}
}
template <class T>
T* ndbuffer_image_impl_base_1<T>::get_pointer(const ndbuffer_image_info_t* ima, std::ptrdiff_t index)
{
......
......@@ -20,6 +20,9 @@ namespace mln
{
D m_subdomain;
template <class I2, class D2>
friend class clip_view;
public:
/// Type definitions
/// \{
......@@ -60,14 +63,7 @@ namespace mln
template <class I2, class D2>
clip_view(const clip_view<I2, D2>& other, const image_build_params& params)
: clip_view::image_adaptor{imchvalue<value_type>(other, params).build()}
, m_subdomain{other.m_subdomain}
{
}
template <class I2, class D2>
clip_view(const clip_view<I2, D2>& other, const value_type& v)
: clip_view::image_adaptor{static_cast<I>((other.base().template ch_value<value_type>()).init(v))}
: clip_view::image_adaptor{imchvalue<value_type>(other.base()).set_params(params).build()}
, m_subdomain{other.m_subdomain}
{
}
......
......@@ -36,8 +36,10 @@ namespace mln
using extension_type = extension::by_value<image_value_t<I>>;
using value_type = image_value_t<I>;
using reference = const value_type; // Restrict the image to be read-only by copy, should not be const and be
// checked on pixel concept (but issue with proxy)
// FIXME: use common_reference here between image_reference_t<I> AND value_type&
using reference = image_reference_t<I>; // Not write only (we may want an out of bound value while keeping the
// the writability of the image)
using category_type = std::common_type_t<image_category_t<I>, bidirectional_image_tag>;
using typename image_adaptor<I>::domain_type;
......
#pragma once
#include <mln/core/image/image.hpp>
#include <mln/core/image/view/value_extended.hpp>
#include <mln/core/neighborhood/neighborhood.hpp>
#include <mln/core/trace.hpp>
#include <mln/morpho/experimental/canvas/unionfind.hpp>
#include <type_traits>
#include <functional>
namespace mln::labeling::experimental
{
/// 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 <class Label_t, class I, class N, class Compare = std::less<>>
[[gnu::noinline]] //
image_ch_value_t<std::remove_reference_t<I>, Label_t>
local_minima(I&& input, N&& 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 <class Label_t, class I, class N>
[[gnu::noinline]] //
image_ch_value_t<std::remove_reference_t<I>, Label_t>
local_maxima(I&& input, N&& nbh, int& nlabel);
/******************************************/
/**** Implementation ****/
/******************************************/
namespace impl
{
// Output must initialized to Label_t(-1)
template <class I, class O, class N, class Compare>
[[gnu::noinline]] int local_minima(I input, N nbh, O output, Compare cmp)
{
using Label_t = image_value_t<O>;
using point_t = image_point_t<I>;
using mln::morpho::experimental::canvas::impl::zfindroot;
constexpr Label_t kUnseen = static_cast<Label_t>(-1);