Index: ChangeLog
from Damien Thivolle <damien(a)lrde.epita.fr>
* oln/convert/ng_to_se.hh: Convert a neighborhood to a structuring
element.
* oln/core/value_box.hh: Fix operator==.
* oln/core/properties.hh: Add window property.
* oln/core/abstract/struct_elt.hh: Add operator-.
* oln/core/abstract/witer.hh (nth): Add method.
* oln/core/abstract/dpoint.hh (nth): Add method.
* oln/core/abstract/neighborhood.hh: Add a window property.
* oln/core/2d/dpoint2d.hh (nth): Add method.
* oln/core/2d/neighborhood2d.hh: Add a window property.
* oln/core/2d/fwd_witer2d.hh: Replace unknown type in constructor.
* oln/utils/clone.hh: Use concrete_type.
* oln/makefile.src: Add new files.
* oln/morpho/reconstruction.hh: New. Sequential and hybrid versions.
* oln/morpho/splitse.hh: New. Split a structuring element.
* oln/morpho/stat.hh: Typo fix.
* oln/morpho/erosion.hh: Fix erroneus function name .
* oln/io/write_image_2d_pnm.hh: Invert binary values (0=>1, 1=>0).
* oln/io/read_image_2d_pnm.hh: Invert binary values.
convert/ng_to_se.hh | 88 +++++
core/2d/dpoint2d.hh | 21 +
core/2d/fwd_witer2d.hh | 4
core/2d/neighborhood2d.hh | 18 -
core/abstract/dpoint.hh | 5
core/abstract/neighborhood.hh | 22 -
core/abstract/struct_elt.hh | 38 +-
core/abstract/witer.hh | 13
core/properties.hh | 1
core/value_box.hh | 18 -
io/read_image_2d_pnm.hh | 4
io/write_image_2d_pnm.hh | 2
makefile.src | 4
morpho/erosion.hh | 6
morpho/reconstruction.hh | 646 ++++++++++++++++++++++++++++++++++++++++++
morpho/splitse.hh | 208 +++++++++++++
morpho/stat.hh | 2
utils/clone.hh | 7
18 files changed, 1062 insertions, 45 deletions
Index: oln/convert/ng_to_se.hh
--- oln/convert/ng_to_se.hh (revision 0)
+++ oln/convert/ng_to_se.hh (revision 0)
@@ -0,0 +1,88 @@
+// Copyright (C) 2002, 2004, 2005 EPITA Research and Development Laboratory
+//
+// This file is part of the Olena Library. This library is free
+// software; you can redistribute it and/or modify it under the terms
+// of the GNU General Public License version 2 as published by the
+// Free Software Foundation.
+//
+// This library 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 this library; see the file COPYING. If not, write to
+// the Free Software Foundation, 59 Temple Place - Suite 330, Boston,
+// MA 02111-1307, USA.
+//
+// As a special exception, you may use this file as part of a free
+// software library 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 OLENA_CONVERT_NG_TO_SE_HH
+# define OLENA_CONVERT_NG_TO_SE_HH
+
+# include <oln/basics.hh>
+
+// because of the internal function in this file
+//# include <oln/basics1d.hh>
+# include <oln/basics2d.hh>
+//# include <oln/basics3d.hh>
+//# include <oln/core/1d/neighborhood1d.hh>
+# include <oln/core/2d/neighborhood2d.hh>
+//# include <oln/core/3d/neighborhood3d.hh>
+
+namespace oln {
+ namespace convert {
+ /*! Convert a neighborhood to a window.
+ **
+ ** \see ng_to_cse
+ */
+ template<class N>
+ oln_type_of(N, window)
+ ng_to_se(const oln::abstract::neighborhood<N>& ng)
+ {
+ oln_type_of(N, window) output;
+
+ for (unsigned i = 0; i < ng.card(); i++)
+ output.add(ng[i]);
+ return output;
+ }
+
+ void dpoint_zero(dpoint2d& dp)
+ {
+ dp.row() = 0;
+ dp.col() = 0;
+ }
+
+ /*! Convert a neighborhood to a window and add the center.
+ **
+ ** \see ng_to_cs
+ */
+ template<class N>
+ oln_type_of(N, window)
+ ng_to_cse(const oln::abstract::neighborhood<N>& ng)
+ {
+ oln_type_of(N, window) output;
+
+ for (unsigned i = 0; i < ng.card(); i++)
+ output.add(ng[i]);
+
+ oln_type_of(N, dpoint) zero;
+ dpoint_zero(zero);
+ output.add(zero);
+ return output;
+ }
+
+
+ } // convert
+} // oln
+
+
+#endif // OLENA_CONVERT_NG_TO_SE_HH
Index: oln/core/value_box.hh
--- oln/core/value_box.hh (revision 106)
+++ oln/core/value_box.hh (working copy)
@@ -81,11 +81,17 @@
/// op==
- bool operator==(const value_box<const I>& rhs) const
+ bool operator==(const value_box<I>& rhs) const
{
return this->value() == rhs.value();
}
+ template <typename II>
+ bool operator==(const value_box<II>& rhs) const
+ {
+ return this->value() < rhs.value();
+ }
+
template <typename V>
bool operator==(const V& rhs) const
{
@@ -113,7 +119,7 @@
template <typename II>
bool operator<(const value_box<II>& rhs) const
{
- return this->value() < value.value();
+ return this->value() < rhs.value();
}
/*! \brief specialized version for value_box<I>
@@ -122,7 +128,7 @@
*/
bool operator<(const value_box<I>& rhs) const
{
- return this->value() < value.value();
+ return this->value() < rhs.value();
}
/*! \brief op=
@@ -277,6 +283,12 @@
return this->value() == rhs.value();
}
+ template <typename II>
+ bool operator==(const value_box<const II>& rhs) const
+ {
+ return this->value() < rhs.value();
+ }
+
template <typename V>
bool operator==(const V& rhs) const
{
Index: oln/core/properties.hh
--- oln/core/properties.hh (revision 106)
+++ oln/core/properties.hh (working copy)
@@ -68,6 +68,7 @@
struct delegated_type;
struct size_type;
struct se_type;
+ struct window_type;
struct image_constness_type;
struct image_dimension_type;
Index: oln/core/abstract/struct_elt.hh
--- oln/core/abstract/struct_elt.hh (revision 106)
+++ oln/core/abstract/struct_elt.hh (working copy)
@@ -47,7 +47,7 @@
// category
template <typename E>
- struct set_category< abstract::struct_elt<E> >
+ struct set_category< abstract::struct_elt<E> >
{
typedef category::struct_elt ret;
};
@@ -71,7 +71,7 @@
<< " fwd_witer_type = " << typeid(fwd_witer_type).name()
<< "," << std::endl
<< " dpoint_type = " << typeid(dpoint_type).name() <<
" }" << std::endl;
}
-
+
};
mlc_register_prop(category::struct_elt, dpoint_type);
@@ -137,7 +137,7 @@
get_delta() const
{
return this->exact().impl_get_delta();
- }
+ }
coord_t
delta_update(const dpoint_type& dp)
@@ -145,14 +145,36 @@
return this->exact().impl_delta_update(dp);
}
+ exact_type
+ operator-() const
+ {
+ exact_type se(this->exact());
+
+ se.sym();
+ return se;
+ }
+
+ void
+ sym()
+ {
+ this->exact().impl_sym();
+ }
+
protected:
+ void
+ impl_sym()
+ {
+ for (unsigned i = 0; i < this->card(); ++i)
+ dp_[i] = - dp_[i];
+ }
+
bool
impl_has(const dpoint_type& dp) const
{
return std::find(dp_.begin(), dp_.end(), dp) != dp_.end();
- }
-
+ }
+
exact_type&
impl_add(const dpoint_type& dp)
{
@@ -172,14 +194,14 @@
impl_card() const
{
return dp_.size();
- }
+ }
const dpoint_type
impl_at(unsigned i) const
{
precondition(i < this->card());
return dp_[i];
- }
+ }
struct_elt() : dp_(), delta_(0)
{};
@@ -208,6 +230,6 @@
o << "]";
return o;
}
-
+
#endif // ! OLENA_CORE_STRUCT_ELT_HH
Index: oln/core/abstract/witer.hh
--- oln/core/abstract/witer.hh (revision 106)
+++ oln/core/abstract/witer.hh (working copy)
@@ -64,7 +64,7 @@
}
};
- mlc_register_prop(category::witer, se_type);
+ mlc_register_prop(category::witer, se_type);
namespace abstract {
@@ -104,7 +104,12 @@
this->exact().impl_invalidate();
postcondition(! this->is_valid());
}
-
+
+ coord_t nth(unsigned i)
+ {
+ return this->se_[this->pos_].nth(i);
+ }
+
protected:
void impl_start()
@@ -127,8 +132,8 @@
pos_ = se_.card();
}
- witer(const se_type& se)
- : se_(se), pos_(0)
+ witer(const se_type& se)
+ : se_(se), pos_(0)
{}
const se_type& se_;
Index: oln/core/abstract/dpoint.hh
--- oln/core/abstract/dpoint.hh (revision 106)
+++ oln/core/abstract/dpoint.hh (working copy)
@@ -65,6 +65,11 @@
return not this->operator==(rhs);
}
+ coord_t nth(unsigned i) const
+ {
+ return this->exact().impl_nth(i);
+ }
+
protected:
dpoint() {}
Index: oln/core/abstract/neighborhood.hh
--- oln/core/abstract/neighborhood.hh (revision 106)
+++ oln/core/abstract/neighborhood.hh (working copy)
@@ -47,7 +47,7 @@
// category
template <typename E>
- struct set_category< abstract::neighborhood<E> >
+ struct set_category< abstract::neighborhood<E> >
{
typedef category::neighborhood ret;
};
@@ -61,6 +61,7 @@
mlc_decl_prop(category::neighborhood, dpoint_type);
mlc_decl_prop(category::neighborhood, size_type);
+ mlc_decl_prop(category::neighborhood, window_type);
static void echo(std::ostream& ostr)
{
@@ -69,11 +70,12 @@
<< " size_type = " << typeid(size_type).name() <<
"," << std::endl
<< " dpoint_type = " << typeid(dpoint_type).name() <<
" }" << std::endl;
}
-
+
};
mlc_register_prop(category::neighborhood, dpoint_type);
mlc_register_prop(category::neighborhood, size_type);
+ mlc_register_prop(category::neighborhood, window_type);
namespace abstract {
@@ -134,7 +136,7 @@
get_delta() const
{
return this->exact().impl_get_delta();
- }
+ }
coord_t
delta_update(const dpoint_type& dp)
@@ -148,8 +150,8 @@
impl_has(const dpoint_type& dp) const
{
return std::find(dp_.begin(), dp_.end(), dp) != dp_.end();
- }
-
+ }
+
exact_type&
impl_add(const dpoint_type& dp)
{
@@ -161,25 +163,25 @@
this->delta_update(dp);
return this->exact();
}
-
+
coord_t
impl_get_delta() const
{
return delta_;
}
-
+
unsigned
impl_card() const
{
return dp_.size();
- }
+ }
const dpoint_type
impl_at(unsigned i) const
{
precondition(i < this->card());
return dp_[i];
- }
+ }
neighborhood() : dp_(), delta_(0)
{};
@@ -208,6 +210,6 @@
o << "]";
return o;
}
-
+
#endif // ! OLENA_CORE_NEIGHBORHOOD_HH
Index: oln/core/2d/dpoint2d.hh
--- oln/core/2d/dpoint2d.hh (revision 106)
+++ oln/core/2d/dpoint2d.hh (working copy)
@@ -94,10 +94,31 @@
const coord_t row() const { return row_; }
const coord_t col() const { return col_; }
+ //FIXME : name it impl_nth when dpoint2d derives from abstract::dpoint
+ const coord_t nth(unsigned i) const
+ {
+ assert(i < 2);
+ if (i == 0)
+ return row_;
+ else
+ return col_;
+ }
+
+
coord_t& row() { return row_; }
coord_t& col() { return col_; }
+ //FIXME : name it impl_nth when dpoint2d derives from abstract::dpoint
+ coord_t& nth(unsigned i)
+ {
+ assert(i < 2);
+ if (i == 0)
+ return row_;
+ else
+ return col_;
+ }
+
protected:
coord_t row_, col_;
};
Index: oln/core/2d/neighborhood2d.hh
--- oln/core/2d/neighborhood2d.hh (revision 106)
+++ oln/core/2d/neighborhood2d.hh (working copy)
@@ -30,6 +30,7 @@
# include <oln/core/2d/dpoint2d.hh>
# include <oln/core/2d/size2d.hh>
+# include <oln/core/2d/window2d.hh>
# include <oln/core/coord.hh>
# include <oln/core/abstract/neighborhood.hh>
@@ -39,7 +40,7 @@
// category
template <>
- struct set_category< neighborhood2d >
+ struct set_category< neighborhood2d >
{ typedef category::neighborhood ret; };
// super_type
@@ -50,18 +51,19 @@
};
template <>
- struct set_props< category::neighborhood, neighborhood2d >
+ struct set_props< category::neighborhood, neighborhood2d >
: public props_of<category::neighborhood>
{
typedef dpoint2d dpoint_type;
typedef size2d size_type;
+ typedef window2d window_type;
};
class neighborhood2d : public abstract::neighborhood<neighborhood2d>
{
public:
-
+
typedef abstract::neighborhood<neighborhood2d> super_type;
/*!
@@ -86,13 +88,13 @@
{
for (unsigned i = 0; i < 2 * n; i += 2)
this->add(dpoint_type(crd[i], crd[i+1]));
- }
+ }
neighborhood2d&
add(const dpoint_type& dp)
{
return this->exact().impl_add(dp);
- }
+ }
neighborhood2d&
add(coord_t row, coord_t col)
@@ -100,8 +102,8 @@
dpoint_type dp(row, col);
return add(dp);
}
-
+
/// Return the name of the type.
static std::string
name()
@@ -116,7 +118,7 @@
delta_(abs(dp.col()));
return delta_;
}
-
+
};
inline const neighborhood2d&
@@ -134,7 +136,7 @@
static const neighborhood2d neighb(4, crd);
return neighb;
}
-
+
} // end of oln
#endif // OLENA_CORE_NEIGHBORHOOD2D_HH
Index: oln/core/2d/fwd_witer2d.hh
--- oln/core/2d/fwd_witer2d.hh (revision 106)
+++ oln/core/2d/fwd_witer2d.hh (working copy)
@@ -48,13 +48,13 @@
{
typedef window2d se_type;
};
-
+
struct fwd_witer2d : public abstract::witer< fwd_witer2d >
{
typedef abstract::witer<fwd_witer2d> super_type;
- fwd_witer2d(const se_type& se) :
+ fwd_witer2d(const window2d& se) :
super_type(se)
{
this->exact_ptr = this;
Index: oln/utils/clone.hh
--- oln/utils/clone.hh (revision 106)
+++ oln/utils/clone.hh (working copy)
@@ -60,10 +60,11 @@
namespace impl {
template <typename I>
- struct clone_type : public abstract::image_unary_operator<I, I,
clone_type<I> >
+ struct clone_type
+ : public abstract::image_unary_operator<oln_type_of(I, concrete), I,
clone_type<I> >
// FIXME: use concrete_type; Cf. erosion.hh
{
- typedef abstract::image_unary_operator<I, I, clone_type<I> > super_type;
+ typedef abstract::image_unary_operator<oln_type_of(I, concrete), I,
clone_type<I> > super_type;
clone_type(const abstract::image<I>& input) :
super_type(input)
@@ -72,7 +73,7 @@
void impl_run()
{
- I tmp(this->input.size()); // FIXME: trick
+ oln_type_of(I, concrete) tmp(this->input.size()); // FIXME: trick
this->output = tmp;
oln_type_of(I, fwd_piter) p(this->input.size());
Index: oln/makefile.src
--- oln/makefile.src (revision 106)
+++ oln/makefile.src (working copy)
@@ -13,6 +13,7 @@
basics3d.hh \
config/pconf.hh \
config/system.hh \
+ convert/ng_to_se.hh \
convert/value_to_point.hh \
core/1d/array1d.hh \
core/1d/dpoint1d.hh \
@@ -87,6 +88,9 @@
io/write_image_2d_pnm.hh \
level/compare.hh \
level/fill.hh \
+ morpho/dilation.hh \
morpho/erosion.hh \
+ morpho/reconstruction.hh \
+ morpho/splitse.hh \
morpho/stat.hh \
utils/clone.hh
Index: oln/morpho/reconstruction.hh
--- oln/morpho/reconstruction.hh (revision 0)
+++ oln/morpho/reconstruction.hh (revision 0)
@@ -0,0 +1,646 @@
+// Copyright (C) 2001, 2002, 2004, 2005 EPITA Research and Development Laboratory
+//
+// This file is part of the Olena Library. This library is free
+// software; you can redistribute it and/or modify it under the terms
+// of the GNU General Public License version 2 as published by the
+// Free Software Foundation.
+//
+// This library 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 this library; see the file COPYING. If not, write to
+// the Free Software Foundation, 59 Temple Place - Suite 330, Boston,
+// MA 02111-1307, USA.
+//
+// As a special exception, you may use this filek as part of a free
+// software library 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 OLENA_MORPHO_RECONSTRUCTION_HH
+# define OLENA_MORPHO_RECONSTRUCTION_HH
+
+# include <queue>
+
+# include <mlc/contract.hh>
+# include <oln/convert/ng_to_se.hh>
+# include <oln/morpho/splitse.hh>
+# include <oln/level/compare.hh>
+# include <oln/core/abstract/neighborhood.hh>
+# include <oln/core/properties.hh>
+# include <oln/core/abstract/image_operator.hh>
+# include <oln/utils/clone.hh>
+
+// FIXME: ADD TESTS !!!!
+
+namespace oln {
+
+ namespace morpho {
+
+ // fwd decl
+ template <typename I, typename E> struct reconstruction_ret;
+
+ }
+
+ // category
+ template <typename I, typename E>
+ struct set_category< morpho::reconstruction_ret<I,E> > { typedef
category::image ret; };
+
+ // super_type
+ template <typename I, typename E>
+ struct set_super_type< morpho::reconstruction_ret<I,E> >
+ {
+ typedef abstract::image_binary_operator<I, I, I, morpho::reconstruction_ret<I,
E> > ret;
+ // FIXME: see below
+ };
+
+
+ namespace morpho {
+
+ template <typename I, typename N>
+ struct reconstruction_ret : public abstract::image_binary_operator<I, I, I,
reconstruction_ret<I, N> >
+ // FIXME: abstract::image_binary_operator<oln_type_of(I, concrete), ...
+ {
+ typedef abstract::image_binary_operator<I, I, I, reconstruction_ret<I, N>
> super_type;
+
+ const N ng;
+
+ reconstruction_ret(const abstract::non_vectorial_image<I>& input1,
+ const abstract::non_vectorial_image<I>& input2,
+ const abstract::neighborhood<N>& ng) :
+ super_type(input1.exact(), input2.exact()),
+ ng(ng.exact())
+ {}
+
+ };
+
+ namespace sequential {
+ /*!
+ ** \brief Perform a geodesic reconstruction dilation.
+ **
+ ** Compute the reconstruction by dilation of marker with respect
+ ** to the mask image using se as structuring element. Soille
+ ** p.160. The algorithm used is the one defined as sequential in
+ ** Vincent(1993), Morphological grayscale reconstruction in
+ ** image analysis: applications and efficient algorithms, itip,
+ ** 2(2), 176--201.
+ **
+ ** \pre Mask must be greater or equal than marker.
+ **
+ ** \param I1 Exact type of image marker.
+ ** \param I2 Exact type of image mask.
+ ** \param N Exact type of neighborhood.
+ **
+ ** \arg marker Image to work on.
+ ** \arg mask Image used for geodesic dilation.
+ ** \arg Ng Neighborhood to use.
+ **
+ ** \code
+ ** #include <oln/basics2d.hh>
+ ** #include <oln/morpho/opening.hh>
+ ** #include <oln/morpho/reconstruction.hh>
+ ** #include <oln/level/compare.hh>
+ ** #include <ntg/all.hh>
+ ** int main()
+ ** {
+ ** typedef oln::image2d<ntg::int_u8> im_type;
+ **
+ ** im_type im1(oln::load(IMG_IN "lena128.pgm"));
+ ** im_type im2 = oln::morpho::opening(im1, oln::win_c4p());
+ **
+ ** oln::save(oln::morpho::sequential::geodesic_reconstruction_dilation(im2,
+ ** im1,
+ **
oln::neighb_c4()),
+ ** IMG_OUT
"oln_morpho_sequential_geodesic_reconstruction_dilation.pbm");
+ ** return 0;
+ ** }
+ ** \endcode
+ **
+ ** \image html lena128_pgm.png
+ ** \image latex lena128_pgm.png
+ ** =>
+ ** \image html oln_morpho_sequential_geodesic_reconstruction_dilation.png
+ ** \image latex oln_morpho_sequential_geodesic_reconstruction_dilation.png
+ **
+ */
+
+ namespace impl {
+
+ template <typename I, typename N>
+ struct reconstruction_dilation_ret : public reconstruction_ret<I, N>
+ {
+ typedef reconstruction_ret<I, N> super_type;
+
+ reconstruction_dilation_ret(const abstract::non_vectorial_image<I>& input1,
//marker
+ const abstract::non_vectorial_image<I>& input2, //mask
+ const abstract::neighborhood<N>& ng)
+
+ : super_type(input1, input2, ng)
+ {}
+
+ void impl_run()
+ {
+ mlc::is_true<mlc::type::eq<oln_type_of(I, size),
+ oln_type_of(N, size)>::ret>::ensure();
+ precondition(this->input1.size() == this->input2.size());
+ precondition(level::is_greater_or_equal(this->input2, this->input1));
+
+ // Conversion of neighborhood into a SE.
+ oln_type_of(N, window) se_plus = get_plus_se_p(convert::ng_to_cse(this->ng));
+ oln_type_of(N, window) se_minus = get_minus_se_p(convert::ng_to_cse(this->ng));
+
+ I output;
+ output = utils::clone(this->input1);
+ bool non_stability = true;
+ oln_type_of(I, fwd_piter) fwd_p(output.size());
+ oln_type_of(I, fwd_piter) bkd_p(output.size());
+ while (non_stability)
+ {
+ I work;
+ work = utils::clone(output);
+ for_all (fwd_p)
+ work[fwd_p] = ntg::min(morpho::max(work, fwd_p, se_plus),
this->input2[fwd_p].value());
+ for_all (bkd_p)
+ work[bkd_p] = ntg::min(morpho::max(work, bkd_p, se_minus),
this->input2[bkd_p].value());
+ non_stability = !(level::is_equal(work, output));
+ output = work;
+ }
+ this->output = output;
+ }
+ };
+
+ }
+
+ template<class I, class N>
+ reconstruction_ret<I, N>
+ geodesic_reconstruction_dilation(const abstract::non_vectorial_image<I> &
marker,
+ const abstract::non_vectorial_image<I> & mask,
+ const abstract::neighborhood<N>& ng)
+ {
+ impl::reconstruction_dilation_ret<I, N> tmp(marker, mask, ng);
+ tmp.run();
+ return tmp;
+ }
+ }// sequential
+
+
+ namespace hybrid {
+
+ namespace internal {
+
+ /*!
+ ** \brief Check if it exists initialization for dilation.
+ **
+ ** \arg p Point to consider.
+ ** \arg marker Image to work on.
+ ** \arg mask Image used as mask.
+ ** \arg Ng Neighborhood to use.
+ */
+ template<class P, class I1, class I2, class E> inline
+ static bool
+ exist_init_dilation(const abstract::point<P>& p,
+ const abstract::non_vectorial_image<I1>& marker,
+ const abstract::non_vectorial_image<I2>& mask,
+ const abstract::struct_elt<E>& se)
+ {
+ oln_type_of(E, fwd_witer) dp(se.exact());
+ for_all (dp)
+ {
+ P q = (oln_type_of(E, dpoint))dp + p.exact();
+ if (marker.hold(q) && (marker[q] < marker[p.exact()]) &&
+ (marker[q] < mask[q]))
+ return true;
+ }
+ return false;
+ }
+
+ } //internal
+
+ /*!
+ ** \brief Perform a geodesic reconstruction dilation.
+ **
+ ** Compute the reconstruction by dilation of marker with
+ ** respect to the mask image using se as structuring
+ ** element. Soille p.160. The algorithm used is the one defined
+ ** as hybrid in Vincent(1993), Morphological grayscale
+ ** reconstruction in image analysis: applications and efficient
+ ** algorithms, itip, 2(2), 176--201.
+ **
+ ** \pre Mask must be greater or equal than marker.
+ **
+ ** \param I1 Exact type of image marker.
+ ** \param I2 Exact type of image mask.
+ ** \param N Exact type of neighborhood.
+ **
+ ** \arg marker Image to work on.
+ ** \arg mask Image used for geodesic dilation.
+ ** \arg Ng Neighborhood to use.
+ **
+ ** \code
+ ** #include <oln/basics2d.hh>
+ ** #include <oln/morpho/opening.hh>
+ ** #include <oln/morpho/reconstruction.hh>
+ ** #include <oln/level/compare.hh>
+ ** #include <ntg/all.hh>
+ ** int main()
+ ** {
+ ** typedef oln::image2d<ntg::int_u8> im_type;
+ **
+ ** im_type im1(oln::load(IMG_IN "lena128.pgm"));
+ ** im_type im2 = oln::morpho::opening(im1, oln::win_c4p());
+ **
+ ** oln::save(oln::morpho::hybrid::geodesic_reconstruction_dilation(im2,
+ ** im1,
+ **
oln::neighb_c4()),
+ ** IMG_OUT
"oln_morpho_hybrid_geodesic_reconstruction_dilation.pbm");
+ ** return 0;
+ ** }
+ ** \endcode
+ **
+ ** \image html lena128_pgm.png
+ ** \image latex lena128_pgm.png
+ ** =>
+ ** \image html oln_morpho_hybrid_geodesic_reconstruction_dilation.png
+ ** \image latex oln_morpho_hybrid_geodesic_reconstruction_dilation.png
+ **
+ */
+
+ namespace impl {
+
+ template <typename I, typename N>
+ struct reconstruction_dilation_ret : public reconstruction_ret<I, N>
+ {
+ typedef reconstruction_ret<I, N> super_type;
+
+ reconstruction_dilation_ret(const abstract::non_vectorial_image<I>& input1,
//marker
+ const abstract::non_vectorial_image<I>& input2, //mask
+ const abstract::neighborhood<N>& ng)
+
+ : super_type(input1, input2, ng)
+ {}
+
+ void impl_run()
+ {
+ mlc::is_true<mlc::type::eq<oln_type_of(I, size),
+ oln_type_of(N, size)>::ret>::ensure();
+ precondition(this->input1.size() == this->input2.size());
+ precondition(level::is_greater_or_equal(this->input2, this->input1));
+
+ oln_type_of(I, concrete) output;
+ output = utils::clone(this->input1);
+ {
+ oln_type_of(N, window) se_plus = get_plus_se_p(convert::ng_to_cse(this->ng));
+ oln_type_of(N, window) se_minus =
get_minus_se_p(convert::ng_to_cse(this->ng));
+ oln_type_of(I, fwd_piter) fwd_p(output.size());
+ oln_type_of(I, fwd_piter) bkd_p(output.size());
+
+ for_all (fwd_p)
+ output[fwd_p] = ntg::min(morpho::max(output, fwd_p, se_plus),
+ this->input2[fwd_p].value());
+
+ std::queue<oln_type_of(I, point) > fifo;
+ for_all (bkd_p)
+ {
+ output[bkd_p] = ntg::min(morpho::max(output, bkd_p, se_minus),
+ this->input2[bkd_p].value());
+ if (internal::exist_init_dilation((oln_type_of(I, point))bkd_p, output,
+ this->input2, se_minus))
+ fifo.push(bkd_p);
+ }
+ // Propagation Step
+ while (!fifo.empty())
+ {
+ oln_type_of(I, point) p = fifo.front();
+ fifo.pop();
+ typedef oln_type_of(N, window) window_type;
+ window_type w = convert::ng_to_se(this->ng);
+ oln_type_of(window_type, fwd_witer) dp(w);
+
+ for_all (dp)
+ {
+ oln_type_of(I, point) q = (oln_type_of(window_type, dpoint))dp + p;
+ if (output.hold(q))
+ {
+ if ((output[q] < output[p]) &&
+ (this->input2[q] != output[q]))
+ {
+ output[q] = ntg::min(output[p].value(),
+ this->input2[q].value());
+ fifo.push(q);
+ }
+ }
+ }
+ }
+ }
+ this->output = output;
+ }
+ };
+
+ }
+
+ template<class I, class N>
+ reconstruction_ret<I, N>
+ geodesic_reconstruction_dilation(const abstract::non_vectorial_image<I> &
marker,
+ const abstract::non_vectorial_image<I> & mask,
+ const abstract::neighborhood<N>& ng)
+ {
+ impl::reconstruction_dilation_ret<I, N> tmp(marker, mask, ng);
+ tmp.run();
+ return tmp;
+ }
+ }// hybrid
+
+
+ namespace sequential {
+
+ /*!
+ ** \brief Perform a geodesic reconstruction erosion.
+ **
+ ** Compute the reconstruction by erosion of marker with respect
+ ** to the mask image using se as structuring element. Soille
+ ** p.160. The algorithm used is the one defined as sequential
+ ** in Vincent(1993), Morphological grayscale reconstruction in
+ ** image analysis: applications and efficient algorithms, itip,
+ ** 2(2), 176--201.
+ **
+ ** \pre Marker must be greater or equal than mask.
+ **
+ ** \param I1 Exact type of image marker.
+ ** \param I2 Exact type of image mask.
+ ** \param N Exact type of neighborhood.
+ **
+ ** \arg marker Image to work on.
+ ** \arg mask Image used for geodesic erosion.
+ ** \arg Ng Neighborhood to use.
+ **
+ ** \code
+ ** #include <oln/basics2d.hh>
+ ** #include <oln/morpho/opening.hh>
+ ** #include <oln/morpho/reconstruction.hh>
+ ** #include <oln/level/compare.hh>
+ ** #include <ntg/all.hh>
+ ** int main()
+ ** {
+ ** typedef oln::image2d<ntg::int_u8> im_type;
+ **
+ ** im_type im1(oln::load(IMG_IN "lena128.pgm"));
+ ** im_type im2 = oln::morpho::opening(im1, oln::win_c4p());
+ **
+ ** oln::save(oln::morpho::sequential::geodesic_reconstruction_erosion(im1,
+ ** im2,
+ **
oln::neighb_c4()),
+ ** IMG_OUT
"oln_morpho_sequential_geodesic_reconstruction_erosion.pbm");
+ ** return 0;
+ ** }
+ ** \endcode
+ **
+ ** \image html lena128_pgm.png
+ ** \image latex lena128_pgm.png
+ ** =>
+ ** \image html oln_morpho_sequential_geodesic_reconstruction_erosion.png
+ ** \image latex oln_morpho_sequential_geodesic_reconstruction_erosion.png
+ **
+ */
+
+
+ namespace impl {
+
+ template <typename I, typename N>
+ struct reconstruction_erosion_ret : public reconstruction_ret<I, N>
+ {
+ typedef reconstruction_ret<I, N> super_type;
+
+ reconstruction_erosion_ret(const abstract::non_vectorial_image<I>& input1,
//marker
+ const abstract::non_vectorial_image<I>& input2, //mask
+ const abstract::neighborhood<N>& ng)
+
+ : super_type(input1, input2, ng)
+ {}
+
+ void impl_run()
+ {
+ mlc::is_true<mlc::type::eq<oln_type_of(I, size),
+ oln_type_of(N, size)>::ret>::ensure();
+ precondition(this->input1.size() == this->input2.size());
+ precondition(level::is_greater_or_equal(this->input2, this->input1));
+
+ // Conversion of neighborhood into a SE.
+ oln_type_of(N, window) se_plus = get_plus_se_p(convert::ng_to_cse(this->ng));
+ oln_type_of(N, window) se_minus = get_minus_se_p(convert::ng_to_cse(this->ng));
+
+ I output;
+ output = utils::clone(this->input1);
+ bool non_stability = true;
+ oln_type_of(I, fwd_piter) fwd_p(output.size());
+ oln_type_of(I, fwd_piter) bkd_p(output.size());
+ while (non_stability)
+ {
+ I work;
+ work = utils::clone(output);
+ for_all (fwd_p)
+ work[fwd_p] = ntg::max(morpho::min(work, fwd_p, se_plus),
+ this->input2[fwd_p].value());
+ for_all (bkd_p)
+ work[bkd_p] = ntg::max(morpho::min(work, bkd_p, se_minus),
+ this->input2[bkd_p].value());
+ non_stability = !(level::is_equal(work, output));
+ output = work;
+ }
+ this->output = output;
+ }
+ };
+
+ }
+
+ template<class I, class N>
+ reconstruction_ret<I, N>
+ geodesic_reconstruction_erosion(const abstract::non_vectorial_image<I> &
marker,
+ const abstract::non_vectorial_image<I> & mask,
+ const abstract::neighborhood<N>& ng)
+ {
+ impl::reconstruction_erosion_ret<I, N> tmp(marker, mask, ng);
+ tmp.run();
+ return tmp;
+ }
+
+ } // sequential
+
+ namespace hybrid {
+
+ namespace internal {
+
+ /*!
+ ** \brief Check if it exists initialization for erosion.
+ **
+ ** \arg p Point to consider.
+ ** \arg marker Image to work on.
+ ** \arg mask Image used as mask.
+ ** \arg Ng Neighborhood to use.
+ */
+ template<class P, class I1, class I2, class E> inline
+ static bool
+ exist_init_erosion(const abstract::point<P>& p,
+ const abstract::non_vectorial_image<I1>& marker,
+ const abstract::non_vectorial_image<I2>& mask,
+ const abstract::struct_elt<E>& se)
+ {
+ oln_type_of(E, fwd_witer) dp(se.exact());
+ for_all (dp)
+ {
+ P q = (oln_type_of(E, dpoint))dp + p.exact();
+ if (marker.hold(q) && (marker[q] > marker[p.exact()]) &&
+ (marker[q] > mask[q]))
+ return true;
+ }
+ return false;
+ }
+
+ } //internal
+
+ /*!
+ ** \brief Perform a geodesic reconstruction erosion.
+ **
+ ** Compute the reconstruction by erosion of marker with respect
+ ** to the mask mask image using se as structuring
+ ** element. Soille p.160. The algorithm used is the one defined
+ ** as hybrid in Vincent(1993), Morphological grayscale
+ ** reconstruction in image analysis: applications and efficient
+ ** algorithms, itip, 2(2), 176--201.
+ **
+ ** \pre Marker must be greater or equal than mask.
+ **
+ ** \param I1 Exact type of image marker.
+ ** \param I2 Exact type of image mask.
+ ** \param N Exact type of neighborhood.
+ **
+ ** \arg marker Image to work on.
+ ** \arg mask Image used for geodesic erosion.
+ ** \arg Ng Neighborhood to use.
+ **
+ ** \code
+ ** #include <oln/basics2d.hh>
+ ** #include <oln/morpho/opening.hh>
+ ** #include <oln/morpho/reconstruction.hh>
+ ** #include <oln/level/compare.hh>
+ ** #include <ntg/all.hh>
+ ** int main()
+ ** {
+ ** typedef oln::image2d<ntg::int_u8> im_type;
+ **
+ ** im_type im1(oln::load(IMG_IN "lena128.pgm"));
+ ** im_type im2 = oln::morpho::opening(im1, oln::win_c4p());
+ **
+ ** oln::save(oln::morpho::hybrid::geodesic_reconstruction_erosion(im1,
+ ** im2,
+ **
oln::neighb_c4()),
+ ** IMG_OUT
"oln_morpho_hybrid_geodesic_reconstruction_erosion.pbm");
+ ** return 0;
+ ** }
+ ** \endcode
+ **
+ ** \image html lena128_pgm.png
+ ** \image latex lena128_pgm.png
+ ** =>
+ ** \image html oln_morpho_hybrid_geodesic_reconstruction_erosion.png
+ ** \image latex oln_morpho_hybrid_geodesic_reconstruction_erosion.png
+ **
+ */
+
+ namespace impl {
+
+ template <typename I, typename N>
+ struct reconstruction_erosion_ret : public reconstruction_ret<I, N>
+ {
+ typedef reconstruction_ret<I, N> super_type;
+
+ reconstruction_erosion_ret(const abstract::non_vectorial_image<I>& input1,
//marker
+ const abstract::non_vectorial_image<I>& input2, //mask
+ const abstract::neighborhood<N>& ng)
+
+ : super_type(input1, input2, ng)
+ {}
+
+ void impl_run()
+ {
+ mlc::is_true<mlc::type::eq<oln_type_of(I, size),
+ oln_type_of(N, size)>::ret>::ensure();
+ precondition(this->input1.size() == this->input2.size());
+ precondition(level::is_greater_or_equal(this->input2, this->input1));
+
+ oln_type_of(I, concrete) output;
+ output = utils::clone(this->input1);
+ {
+ oln_type_of(N, window) se_plus = get_plus_se_p(convert::ng_to_cse(this->ng));
+ oln_type_of(N, window) se_minus =
get_minus_se_p(convert::ng_to_cse(this->ng));
+ oln_type_of(I, fwd_piter) fwd_p(output.size());
+ oln_type_of(I, fwd_piter) bkd_p(output.size());
+
+ for_all (fwd_p)
+ output[fwd_p] = ntg::max(morpho::min(output, fwd_p, se_plus),
+ this->input2[fwd_p].value());
+
+ std::queue<oln_type_of(I, point) > fifo;
+ for_all (bkd_p)
+ {
+ output[bkd_p] = ntg::max(morpho::min(output, bkd_p, se_minus),
+ this->input2[bkd_p].value());
+ if (internal::exist_init_erosion((oln_type_of(I, point))bkd_p, output,
+ this->input2, se_minus))
+ fifo.push(bkd_p);
+ }
+ // Propagation Step
+ while (!fifo.empty())
+ {
+ oln_type_of(I, point) p = fifo.front();
+ fifo.pop();
+ typedef oln_type_of(N, window) window_type;
+ window_type w = convert::ng_to_se(this->ng);
+ oln_type_of(window_type, fwd_witer) dp(w);
+
+ for_all (dp)
+ {
+ oln_type_of(I, point) q = (oln_type_of(window_type, dpoint))dp + p;
+ if (output.hold(q))
+ {
+ if ((output[q] > output[p]) &&
+ (this->input2[q] != output[q]))
+ {
+ output[q] = ntg::max(output[p].value(),
+ this->input2[q].value());
+ fifo.push(q);
+ }
+ }
+ }
+ }
+ }
+ this->output = output;
+ }
+ };
+
+ }
+
+ template<class I, class N>
+ reconstruction_ret<I, N>
+ geodesic_reconstruction_erosion(const abstract::non_vectorial_image<I> &
marker,
+ const abstract::non_vectorial_image<I> & mask,
+ const abstract::neighborhood<N>& ng)
+ {
+ impl::reconstruction_erosion_ret<I, N> tmp(marker, mask, ng);
+ tmp.run();
+ return tmp;
+ }
+ }// hybrid
+
+ }
+
+}
+
+#endif // ! OLENA_MORPHO_RECONSTRUCTION_HH
Index: oln/morpho/splitse.hh
--- oln/morpho/splitse.hh (revision 0)
+++ oln/morpho/splitse.hh (revision 0)
@@ -0,0 +1,208 @@
+// Copyright (C) 2001, 2002, 2004, 2005 EPITA Research and Development Laboratory
+//
+// This file is part of the Olena Library. This library is free
+// software; you can redistribute it and/or modify it under the terms
+// of the GNU General Public License version 2 as published by the
+// Free Software Foundation.
+//
+// This library 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 this library; see the file COPYING. If not, write to
+// the Free Software Foundation, 59 Temple Place - Suite 330, Boston,
+// MA 02111-1307, USA.
+//
+// As a special exception, you may use this file as part of a free
+// software library 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 OLENA_MORPHO_SPLITSE_HH
+# define OLENA_MORPHO_SPLITSE_HH
+
+# include <oln/basics.hh>
+
+# include <oln/core/1d/size1d.hh>
+# include <oln/core/2d/size2d.hh>
+# include <oln/core/3d/size3d.hh>
+
+namespace oln {
+ namespace morpho {
+
+ template <class S>
+ struct dim_traits
+ {
+ };
+
+ template <>
+ struct dim_traits<size1d>
+ {
+ enum {dim = 1};
+ };
+
+ template <>
+ struct dim_traits<size2d>
+ {
+ enum {dim = 2};
+ };
+
+ template <>
+ struct dim_traits<size3d>
+ {
+ enum {dim = 3};
+ };
+
+ /*!
+ ** \brief Get a sub part of a structuring element.
+ **
+ ** \param E Exact type of the structuring element.
+ **
+ ** \arg se The structuring element.
+ **
+ ** A point p take part of the new structuring element if it exists
+ ** a i that belongs to [[0..dim-1]] like p(i) < 0 and for all j
+ ** that belongs to [[0..i-1]] p(j) = 0.
+ **
+ */
+
+
+ template<class E>
+ E
+ get_plus_se_only(const abstract::struct_elt<E>& se)
+ {
+ oln_type_of(E, fwd_witer) dp(se.exact());
+ E out;
+
+ for_all (dp)
+ {
+ unsigned n;
+ for (n = 0; n < dim_traits<oln_type_of(E, size)>::dim; ++n)
+ if (dp.nth(n) < 0) {
+ out.add(dp);
+ break;
+ } else if (dp.nth(n) > 0) {
+ break;
+ }
+ }
+ return out;
+ }
+
+ /*!
+ ** \brief Get a sub part of a structuring element.
+ **
+ ** \param E Exact type of the structuring element.
+ **
+ ** \arg se The structuring element.
+ **
+ ** A point p take part of the new structuring element if it exists
+ ** a i that belongs to [[0..dim-1]] like p(i) < 0 and for all j
+ ** that belongs to [[0..i-1]] p(j) = 0 or if for all i that
+ ** belongs to [[0..dim-1]] p(i) = 0.
+ **
+ */
+ template<class E>
+ E
+ get_plus_se_p(const abstract::struct_elt<E>& se)
+ {
+ oln_type_of(E, fwd_witer) dp(se.exact());
+ E out;
+
+ for_all (dp)
+ {
+ unsigned n;
+ for (n = 0; n < dim_traits<oln_type_of(E, size)>::dim; ++n)
+ if (dp.nth(n) < 0) {
+ out.add(dp);
+ break;
+ } else if (dp.nth(n) > 0) {
+ break;
+ }
+ // All p.nth(n) are 0.
+ if (n == dim_traits<oln_type_of(E, size)>::dim)
+ out.add(dp);
+ }
+ return out;
+ }
+
+ /*!
+ ** \brief Get a sub part of a structuring element.
+ **
+ ** \param E Exact type of the structuring element.
+ **
+ ** \arg se The structuring element.
+ **
+ ** A point p take part of the new structuring element if it exists
+ ** a i that belongs to [[0..dim-1]] like p(i) > 0 and for all j
+ ** that belongs to [[0..i-1]] p(j) = 0.
+ **
+ */
+ template<class E>
+ E
+ get_minus_se_only(const abstract::struct_elt<E>& se)
+ {
+ oln_type_of(E, fwd_witer) dp(se.exact());
+ E out;
+
+ for_all (dp)
+ {
+ unsigned n;
+ for (n = 0; n < dim_traits<oln_type_of(E, size)>::dim; ++n)
+ if (dp.nth(n) > 0) {
+ out.add(dp);
+ break;
+ } else if (dp.nth(n) < 0) {
+ break;
+ }
+ }
+ return out;
+ }
+
+ /*!
+ ** \brief Get a sub part of a structuring element.
+ **
+ ** \param E Exact type of the structuring element.
+ **
+ ** \arg se The structuring element.
+ **
+ ** A point p take part of the new structuring element if it exists
+ ** a i that belongs to [[0..dim-1]] like p(i) > 0 and for all j
+ ** that belongs to [[0..i-1]] p(j) = 0 or if for all i that
+ ** belongs to [[0..dim-1]] p(i) = 0.
+ **
+ */
+ template<class E>
+ E
+ get_minus_se_p(const abstract::struct_elt<E>& se)
+ {
+ oln_type_of(E, fwd_witer) dp(se.exact());
+ E out;
+
+ for_all (dp)
+ {
+ unsigned n;
+ for (n = 0; n < dim_traits<oln_type_of(E, size)>::dim; ++n)
+ if (dp.nth(n) > 0) {
+ out.add(dp);
+ break;
+ } else if (dp.nth(n) < 0) {
+ break;
+ }
+ // All p.nth(n) are 0.
+ if (n == dim_traits<oln_type_of(E, size)>::dim)
+ out.add(dp);
+ }
+ return out;
+ }
+
+ } // morpho
+} // oln
+
+#endif // OLENA_MORPHO_SPLITSE_HH
Index: oln/morpho/stat.hh
--- oln/morpho/stat.hh (revision 106)
+++ oln/morpho/stat.hh (working copy)
@@ -47,7 +47,7 @@
** \param E Structuring element type.
** \param V Associated value type.
*/
- template <class I, class E, class V =oln_type_of(I, value)>
+ template <class I, class E, class V = oln_type_of(I, value)>
struct stat_
{
/*!
Index: oln/morpho/erosion.hh
--- oln/morpho/erosion.hh (revision 106)
+++ oln/morpho/erosion.hh (working copy)
@@ -43,7 +43,7 @@
/// Erosion as a procedure (do not use it; prefer morpho::erosion).
template<typename I, typename S>
- oln_type_of(I, concrete) dilation(const abstract::image<I>& input,
+ oln_type_of(I, concrete) erosion(const abstract::image<I>& input,
const abstract::struct_elt<S>& se)
{
oln_type_of(I, concrete) output(input.size());
@@ -131,14 +131,14 @@
typedef typename super_type::output_type output_type;
const E se;
-
+
erosion_ret(const abstract::image<I>& input,
const abstract::struct_elt<E>& se) :
super_type(input),
se(se.exact())
{
}
-
+
};
Index: oln/io/write_image_2d_pnm.hh
--- oln/io/write_image_2d_pnm.hh (revision 106)
+++ oln/io/write_image_2d_pnm.hh (working copy)
@@ -122,7 +122,7 @@
bin_v = 0;
ret = true;
}
- if (c == value_type(1))
+ if (c == value_type(0))
bin_v |= 1 << bin_offset;
bin_offset--;
return ret;
Index: oln/io/read_image_2d_pnm.hh
--- oln/io/read_image_2d_pnm.hh (revision 106)
+++ oln/io/read_image_2d_pnm.hh (working copy)
@@ -172,9 +172,9 @@
offset = 7;
}
if ((int)(v & (1<<offset--)) == 0)
+ c = 0;
+ else
c = 1;
- else
- c = 0;
}
};