// Copyright (C) 2008, 2009, 2012 EPITA Research and Development
// Laboratory (LRDE)
//
// This file is part of Olena.
//
// Olena is free software: you can redistribute it and/or modify it under
// the terms of the GNU General Public License as published by the Free
// Software Foundation, version 2 of the License.
//
// Olena is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
// General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with Olena. If not, see .
//
// As a special exception, you may use this file as part of a free
// software project without restriction. Specifically, if other files
// instantiate templates or use macros or inline functions from this
// file, or you compile this file and link it with other files to produce
// an executable, this file does not by itself cause the resulting
// executable to be covered by the GNU General Public License. This
// exception does not however invalidate any other reasons why the
// executable file might be covered by the GNU General Public License.
#ifndef MLN_MORPHO_WATERSHED_FLOODING_HH
# define MLN_MORPHO_WATERSHED_FLOODING_HH
/// \file
///
/// \brief Meyer's Watershed Transform (WST) algorithm.
///
/// The Watershed Transform algorithm from Meyer using a hierarchical
/// queue.
///
/// Reference:
/// Fernand Meyer. Un algorithme optimal de ligne de partage des
/// eaux. In: Actes du 8ème Congrès AFCET, Lyon-Villeurbanne, France
/// (1991), pages 847--859.
# include
# include
# include
# include
# include
# include
# include
namespace mln
{
namespace morpho
{
namespace watershed
{
/*! \brief Meyer's Watershed Transform (WST) algorithm.
*
* \param[in] input The input image.
* \param[in] nbh The connexity of markers.
* \param[out] n_basins The number of basins.
*
* \li \p L is the type of labels, used to number the watershed
* itself (with the minimal value), and the basins.
* \li \p I is the exact type of the input image.
* \li \p N is the exact type of the neighborhood used to express
* \a input's connexity.
*
* \ingroup mlnmorphowst
*/
template
mln_ch_value(I, L)
flooding(const Image& input, const Neighborhood& nbh,
L& n_basins);
/*! \brief Meyer's Watershed Transform (WST) algorithm, with no
* count of basins.
*
* \param[in] input The input image.
* \param[in] nbh The connexity of markers.
*
* \li \p L is the type of labels, used to number the watershed
* itself (with the minimal value), and the basins.
* \li \p I is the exact type of the input image.
* \li \p N is the exact type of the neighborhood used to express
* \a input's connexity.
*
* Note that the first parameter, \p L, is not automatically
* valued from the type of the actual argument during implicit
* instantiation: you have to explicitly pass this parameter at
* call sites.
*
* \ingroup mlnmorphowst
*/
template
mln_ch_value(I, L)
flooding(const Image& input, const Neighborhood& nbh);
# ifndef MLN_INCLUDE_ONLY
// Implementations.
namespace impl
{
namespace generic
{
template
mln_ch_value(I, L)
flooding(const Image& input_, const Neighborhood& nbh_,
L& n_basins)
{
mln_trace("morpho::watershed::impl::generic::flooding");
/* FIXME: Ensure the input image has scalar values. */
const I input = exact(input_);
const N nbh = exact(nbh_);
typedef L marker;
const marker unmarked = literal::zero;
typedef mln_value(I) V;
const V max = mln_max(V);
// Initialize the output with the markers (minima
// components).
typedef mln_ch_value(I, L) O;
O output = labeling::regional_minima(input, nbh, n_basins);
typedef mln_psite(I) psite;
// Ordered queue.
typedef p_queue_fast Q;
p_priority queue;
// In_queue structure to avoid processing sites several times.
mln_ch_value(I, bool) in_queue;
initialize(in_queue, input);
data::fill(in_queue, false);
// Insert every neighbor P of every marked area in a
// hierarchical queue, with a priority level corresponding to
// the grey level input(P).
mln_piter(I) p(output.domain());
mln_niter(N) n(nbh, p);
for_all(p)
if (output(p) == unmarked)
for_all(n)
if (output.domain().has(n) && output(n) != unmarked)
{
queue.push(max - input(p), p);
in_queue(p) = true;
break;
}
/* Until the queue is empty, extract a psite P from the
hierarchical queue, at the highest priority level, that is,
the lowest level. */
while (! queue.is_empty())
{
psite p = queue.front();
queue.pop();
// Last seen marker adjacent to P.
marker adjacent_marker = unmarked;
// Has P a single adjacent marker?
bool single_adjacent_marker_p = true;
mln_niter(N) n(nbh, p);
for_all(n)
if (output.domain().has(n) && output(n) != unmarked)
{
if (adjacent_marker == unmarked)
{
adjacent_marker = output(n);
single_adjacent_marker_p = true;
}
else
if (adjacent_marker != output(n))
{
single_adjacent_marker_p = false;
break;
}
}
/* If the neighborhood of P contains only psites with the
same label, then P is marked with this label, and its
neighbors that are not yet marked are put into the
hierarchical queue. */
if (single_adjacent_marker_p)
{
output(p) = adjacent_marker;
for_all(n)
if (output.domain().has(n) && output(n) == unmarked
&& ! in_queue(n))
{
queue.push(max - input(n), n);
in_queue(n) = true;
}
}
}
return output;
}
} // end of namespace mln::morpho::watershed::impl::generic
// Fastest version.
template
mln_ch_value(I, L)
flooding_fastest(const Image& input_, const Neighborhood& nbh_,
L& n_basins)
{
mln_trace("morpho::watershed::impl::flooding_fastest");
/* FIXME: Ensure the input image has scalar values. */
const I input = exact(input_);
const N nbh = exact(nbh_);
typedef L marker;
const marker unmarked = literal::zero;
typedef mln_value(I) V;
const V max = mln_max(V);
extension::adjust_fill(input, nbh, max);
// Initialize the output with the markers (minima components).
typedef mln_ch_value(I, L) O;
O output = labeling::regional_minima(input, nbh, n_basins);
extension::fill(output, unmarked);
// Ordered queue.
typedef p_queue_fast Q;
p_priority queue;
// FIXME: With typedef std::pair VU;
// std::priority_queue queue;
// we do not get the same results!!!
// In_queue structure to avoid processing sites several times.
mln_ch_value(I, bool) in_queue;
initialize(in_queue, input);
data::fill(in_queue, false);
extension::fill(in_queue, true);
// Insert every neighbor P of every marked area in a
// hierarchical queue, with a priority level corresponding to
// the grey level input(P).
mln_pixter(const O) p_out(output);
mln_nixter(const O, N) n_out(p_out, nbh);
for_all(p_out)
if (p_out.val() == unmarked)
for_all(n_out)
if (n_out.val() != unmarked)
{
unsigned po = p_out.offset();
queue.push(max - input.element(po), po);
in_queue.element(po) = true;
break;
}
/* Until the queue is empty, extract a psite P from the
hierarchical queue, at the highest priority level, that is,
the lowest level. */
util::array dp = offsets_wrt(input, nbh);
const unsigned n_nbhs = dp.nelements();
while (! queue.is_empty())
{
unsigned p = queue.front();
queue.pop();
// Last seen marker adjacent to P.
marker adjacent_marker = unmarked;
// Has P a single adjacent marker?
bool single_adjacent_marker_p = true;
for (unsigned i = 0; i < n_nbhs; ++i)
{
unsigned n = p + dp[i];
// In the border, OUTPUT is unmarked so N is ignored.
if (output.element(n) != unmarked)
{
if (adjacent_marker == unmarked)
{
adjacent_marker = output.element(n);
single_adjacent_marker_p = true;
}
else
if (adjacent_marker != output.element(n))
{
single_adjacent_marker_p = false;
break;
}
}
}
/* If the neighborhood of P contains only psites with the
same label, then P is marked with this label, and its
neighbors that are not yet marked are put into the
hierarchical queue. */
if (single_adjacent_marker_p)
{
output.element(p) = adjacent_marker;
for (unsigned i = 0; i < n_nbhs; ++i)
{
unsigned n = p + dp[i];
if (output.element(n) == unmarked
// In the border, IN_QUEUE is true so N is ignored.
&& ! in_queue.element(n))
{
queue.push(max - input.element(n), n);
in_queue.element(n) = true;
}
}
}
}
return output;
}
} // end of namespace mln::morpho::watershed::impl
// Dispatch.
namespace internal
{
template
inline
mln_ch_value(I, L)
flooding_dispatch(metal::false_,
const Image& input, const Neighborhood& nbh,
L& n_basins)
{
return impl::generic::flooding(input, nbh, n_basins);
}
template
inline
mln_ch_value(I, L)
flooding_dispatch(metal::true_,
const Image& input, const Neighborhood& nbh,
L& n_basins)
{
return impl::flooding_fastest(input, nbh, n_basins);
}
template
inline
mln_ch_value(I, L)
flooding_dispatch(const Image& input, const Neighborhood& nbh,
L& n_basins)
{
enum {
test = mlc_equal(mln_trait_image_speed(I),
trait::image::speed::fastest)::value
&&
mln_is_simple_neighborhood(N)::value
};
return flooding_dispatch(metal::bool_(),
input, nbh, n_basins);
}
} // end of namespace mln::morpho::watershed::internal
// Facades.
template
inline
mln_ch_value(I, L)
flooding(const Image& input, const Neighborhood& nbh, L& n_basins)
{
mln_trace("morpho::watershed::flooding");
// FIXME: internal::flooding_tests(input, nbh, n_basins);
mln_ch_value(I, L) output =
internal::flooding_dispatch(input, nbh, n_basins);
return output;
}
template
mln_ch_value(I, L)
flooding(const Image& input, const Neighborhood& nbh)
{
L nbasins;
return flooding(input, nbh, nbasins);
}
# endif // ! MLN_INCLUDE_ONLY
} // end of namespace mln::morpho::watershed
} // end of namespace mln::morpho
} // end of namespace mln
#endif // ! MLN_MORPHO_WATERSHED_FLOODING_HH