#137: Write more attribute (connected) filters
----------------------+-----------------------------------------------------
Reporter: levill_r | Owner: Olena Team
Type: task | Status: new
Priority: major | Milestone: Olena 1.0
Component: Milena | Version: 1.0
Keywords: |
----------------------+-----------------------------------------------------
For instance,
* a volume opening/closing;
* a filter based on the height of a component;
* etc.
--
Ticket URL: <https://trac.lrde.org/olena/ticket/137>
Olena <http://olena.lrde.epita.fr>
Olena, a generic and efficient C++ image processing library.
https://svn.lrde.epita.fr/svn/oln/trunk/milena
Index: ChangeLog
from Roland Levillain <roland(a)lrde.epita.fr>
Add an extra test on Meyer's WST on a synthetic line graph image.
* tests/morpho/artificial_line_graph_image_wst.cc: New test.
* tests/morpho/Makefile.am (check_PROGRAMS): Add
artificial_line_graph_image_wst.
(artificial_line_graph_image_wst_SOURCES)
(artificial_line_graph_image_wst_CXXFLAGS):
New.
(lena_line_graph_image_wst2_CXXFLAGS): Typo.
Makefile.am | 6 -
artificial_line_graph_image_wst.cc | 222 +++++++++++++++++++++++++++++++++++++
2 files changed, 227 insertions(+), 1 deletion(-)
Index: tests/morpho/artificial_line_graph_image_wst.cc
--- tests/morpho/artificial_line_graph_image_wst.cc (revision 0)
+++ tests/morpho/artificial_line_graph_image_wst.cc (revision 0)
@@ -0,0 +1,222 @@
+// Copyright (C) 2008 EPITA Research and Development Laboratory (LRDE)
+//
+// 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, 51 Franklin Street, Fifth Floor,
+// 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.
+
+/* FIXME: We should factor as much things as possible between
+ tests/morpho/lena_line_graph_image_wst1.cc,
+ tests/morpho/lena_line_graph_image_wst2.cc and this file, starting
+ with conversion routines. */
+
+/** \file tests/morpho/artificial_line_graph_image_wst.cc
+ \brief Tests on the Watershed Transform (WST) on an artificial
+ mln::line_graph_image.
+
+ The scenario is as follows:
+ \li create an artificial (checkboard) 2-D image ;
+ \li convert this 2-D image into a line graph-based one, where values
+ on edges are computed as the absolute value of the difference
+ between the values on the nodes adjacent to the edge, so as to
+ create a (norm of the) gradient ``between the pixels'' of the
+ input image;
+ \li perform a WST on this line graph image;
+ \li create a 2-D, color output image with height and width double
+ the size the original one, and copy the data of the input image
+ in it, interpolating inter-pixel points;
+ \li print the watershed on lines into that same image, and save it. */
+
+#include <vector>
+
+#include <mln/core/image2d.hh>
+#include <mln/core/point2d.hh>
+#include <mln/core/window2d.hh>
+#include <mln/core/neighb2d.hh>
+
+#include <mln/core/line_graph_image.hh>
+#include <mln/core/line_graph_elt_neighborhood.hh>
+#include <mln/core/line_graph_neighborhood_piter.hh>
+
+#include <mln/morpho/line_gradient.hh>
+#include <mln/morpho/closing_area_on_vertices.hh>
+#include <mln/morpho/meyer_wst.hh>
+#include <mln/level/stretch.hh>
+
+#include <mln/value/int_u8.hh>
+#include <mln/value/int_u16.hh>
+#include <mln/value/rgb8.hh>
+#include <mln/literal/black.hh>
+#include <mln/literal/colors.hh>
+
+#include <mln/io/pgm/load.hh>
+#include <mln/io/pgm/save.hh>
+#include <mln/io/ppm/save.hh>
+
+#include <mln/math/max.hh>
+#include <mln/math/abs.hh>
+
+#include "tests/data.hh"
+
+
+
+int main()
+{
+ using namespace mln;
+ using value::int_u8;
+ using value::int_u16;
+ using value::rgb8;
+
+ /*--------.
+ | Input. |
+ `--------*/
+
+ // Dimensions.
+ const unsigned nrows = 100;
+ const unsigned ncols = 100;
+ const unsigned square_length = 3;
+ typedef int_u8 input_val_t;
+ // Create a checkboard image.
+ image2d<input_val_t> input (nrows, ncols);
+ for (unsigned r = 0; r < nrows; ++r)
+ for (unsigned c = 0; c < ncols; ++c)
+ input.at(r,c) =
+ ((r / square_length) % 2 == (c / square_length) % 2)
+ ? mln_min(input_val_t)
+ : mln_max(input_val_t);
+ std::cout << "nvertices = " << nrows * ncols << std::endl
+ << "nedges = " << 2 * nrows * ncols - (nrows + ncols) << std::endl;
+
+ /*----------------.
+ | Line gradient. |
+ `----------------*/
+
+ // Line graph image.
+ typedef line_graph_image<point2d, input_val_t> ima_t;
+ ima_t lg_ima = morpho::line_gradient(input);
+
+ /*------.
+ | WST. |
+ `------*/
+
+ typedef line_graph_elt_neighborhood<point2d> nbh_t;
+ nbh_t nbh;
+
+ // Perform a Watershed Transform.
+ typedef unsigned wst_val_t;
+ wst_val_t nbasins;
+ typedef line_graph_image<point2d, wst_val_t> wst_ima_t;
+ wst_ima_t wshed = morpho::meyer_wst(lg_ima, nbh, nbasins);
+ std::cout << "nbasins = " << nbasins << std::endl;
+
+ /*---------.
+ | Output. |
+ `---------*/
+
+ // FIXME: Inlined conversion, to be reifed into a routine.
+
+ // Save the result in gray levels (data) + color (wshed).
+
+ // Data.
+ typedef rgb8 output_val_t;
+ typedef image2d<output_val_t> output_t;
+ point2d output_pmin = input.domain().pmin();
+ point2d output_pmax(input.domain().pmax()[0] * 2,
+ input.domain().pmax()[1] * 2);
+ output_t output(box2d(output_pmin, output_pmax));
+ level::fill(output, literal::black);
+ mln_fwd_piter_(image2d<input_val_t>) p(input.domain());
+ for_all(p)
+ {
+ // Equivalent of P in OUTPUT.
+ point2d q(p[0] * 2, p[1] * 2);
+ input_val_t v = input(p);
+ /* FIXME: Use a conversion function from input_val_t to
+ output_val_t instead of an explicit construction. */
+ output(q) = output_val_t(v, v, v);
+ }
+ // Interpolate missing points in OUTPUT.
+ mln_piter_(output_t) p_out(output.domain());
+ for_all(p_out)
+ {
+ // Process points on even rows and odd columns
+ if (p_out[0] % 2 == 0 && p_out[1] % 2 == 1)
+ output(p_out) = (output(p_out + left) + output(p_out + right)) / 2;
+ // Process points on odd rows and even columns
+ if (p_out[0] % 2 == 1 && p_out[1] % 2 == 0)
+ output(p_out) = (output(p_out + up) + output(p_out + down)) / 2;
+ // Process points on odd rows and odd columns
+ if (p_out[0] % 2 == 1 && p_out[1] % 2 == 1)
+ output(p_out) =
+ (output(p_out + make::dpoint2d(-1, -1)) +
+ output(p_out + make::dpoint2d(-1, +1)) +
+ output(p_out + make::dpoint2d(+1, -1)) +
+ output(p_out + make::dpoint2d(+1, +1))) / 4;
+ }
+ // Draw the watershed.
+ /* FIXME: We should draw the watershed on another image and
+ superimpose it on OUTPUT instead of drawing it directly into
+ OUTPUT. */
+ mln_piter_(wst_ima_t) pw(wshed.domain());
+ for_all(pw)
+ {
+ if (wshed(pw) == 0)
+ {
+ mln_psite_(wst_ima_t) pp(pw);
+ // Equivalent of the line (edge) PP in OUTPUT.
+ int row1 = pp.first()[0] * 2;
+ int col1 = pp.first()[1] * 2;
+ int row2 = pp.second()[0] * 2;
+ int col2 = pp.second()[1] * 2;
+ point2d q((row1 + row2) / 2, (col1 + col2) / 2);
+ // Print the watershed in red.
+ output(q) = literal::red;
+ }
+ }
+ // Fill the holes, so that the watershed looks connected.
+ /* FIXME: This approach is bad: it creates thick lines of watershed.
+ We should probably solve this when we ``paint'' the watershed
+ over the ``doubled'' image.
+
+ A better approach is probably to iterate over the set of nodes,
+ and ``connect'' edges according to patterns (vertically or
+ horizontally connected egdes member of the watershed, etc.). */
+ // Reuse the piter on OUTPUT.
+ for_all (p_out)
+ // Only handle points on odd rows and columns.
+ if (p_out[0] % 2 == 1 && p_out[1] % 2 == 1)
+ {
+ // Count the number of adjacent watershed points. If there are
+ // two or more, consider, create a watershed point.
+ /* FIXME: Iterating over a c4 window would be more elegant, of
+ course. */
+ unsigned nwsheds =
+ (output.has(p_out + up ) && output(p_out + up ) == literal::red) +
+ (output.has(p_out + down ) && output(p_out + down ) == literal::red) +
+ (output.has(p_out + left ) && output(p_out + right) == literal::red) +
+ (output.has(p_out + right) && output(p_out + left ) == literal::red);
+ if (nwsheds >= 2)
+ output(p_out) = literal::red;
+ }
+ io::ppm::save(output, "out.ppm");
+}
Index: tests/morpho/Makefile.am
--- tests/morpho/Makefile.am (revision 1985)
+++ tests/morpho/Makefile.am (working copy)
@@ -3,6 +3,7 @@
include $(top_srcdir)/milena/tests/tests.mk
check_PROGRAMS = \
+ artificial_line_graph_image_wst \
closing_area \
combined \
contrast \
@@ -47,6 +48,9 @@
# Complex tests. #
# --------------- #
+artificial_line_graph_image_wst_SOURCES = artificial_line_graph_image_wst.cc
+artificial_line_graph_image_wst_CXXFLAGS = $(TESTS_CXXFLAGS_SPEED)
+
dilation_SOURCES = dilation.cc
dilation_CXXFLAGS = $(TESTS_CXXFLAGS_SPEED)
@@ -54,7 +58,7 @@
lena_line_graph_image_wst1_CXXFLAGS = $(TESTS_CXXFLAGS_SPEED)
lena_line_graph_image_wst2_SOURCES = lena_line_graph_image_wst2.cc
-lena_line_graph_image_wst2_CXXFLAGS = $(TESTSCXXFLAGS_SPEED)
+lena_line_graph_image_wst2_CXXFLAGS = $(TESTS_CXXFLAGS_SPEED)
meyer_wst_long_SOURCES = meyer_wst_long.cc
meyer_wst_long_CXXFLAGS = $(TESTS_CXXFLAGS_SPEED)
https://svn.lrde.epita.fr/svn/oln/trunk/milena
Index: ChangeLog
from Roland Levillain <roland(a)lrde.epita.fr>
Add some comments on mln::canvas::morpho::algebraic_union_find<F>.
* mln/canvas/morpho/algebraic_union_find.hh
(mln::canvas::morpho::algebraic_union_find<F>::make_set)
(mln::canvas::morpho::algebraic_union_find<F>::do_union): Add
FIXMEs on the non genericity of this canvas w.r.t. the type of the
attribute.
algebraic_union_find.hh | 34 ++++++++++++++++++++++++++++++++++
1 file changed, 34 insertions(+)
Index: mln/canvas/morpho/algebraic_union_find.hh
--- mln/canvas/morpho/algebraic_union_find.hh (revision 1982)
+++ mln/canvas/morpho/algebraic_union_find.hh (working copy)
@@ -120,6 +120,28 @@
void make_set(const psite& p)
{
parent(p) = p;
+ /* FIXME: What if the value_type of DATA (i.e., A) were not
+ based on a accu::count_<mln::pix>? Currently, nothing
+ enforces this, but the code below expects this line to be
+ valid:
+
+ data(p).take_as_init(make::pix(f.input, p))
+
+ which probably restricts the kind of input images.
+
+ If we want to be more generic, the initialization should
+ read something like:
+
+ init_data(p);
+
+ i.e., the functor for the initialization of data should
+ be passed as an argument to the canvas' ctor.
+
+ Of course, we might want to restrict attributes to the
+ accumulator accu::count_<mln::pix> (which is perfectly
+ acceptable), but then this class should statically check
+ the conformance of the template parameter A to this
+ constraint. */
data(p).take_as_init(make::pix(f.input, p)); // FIXME: algebraic so p!
}
@@ -149,6 +171,18 @@
{
if (equiv(r, p))
{
+ /* FIXME: Same remark as above concerning the
+ initialization of data(p); instead of
+
+ data(p).take(data(r));
+
+ we should (or could) have
+
+ unite_data(p, r);
+
+ so as to keep the generic aspect of this canvas
+ (as long as the set of acceptable types for the
+ template parameter A is not bound). */
data(p).take(data(r));
parent(r) = p;
}
https://svn.lrde.epita.fr/svn/oln/trunk/milena/sandbox
Those files were in my working copy for ages... I'll try to finish the
conversion from Olena proto-1.0 to Milena someday.
Index: ChangeLog
from Roland Levillain <roland(a)lrde.epita.fr>
Add some morphological routines to Roland's sandbox.
* levillain/morpho/: New directory.
* levillain/morpho/lower_completion.hh,
* levillain/morpho/shortest_path_watershed.hh:
New (imported from Olena proto-1.0).
lower_completion.hh | 29 +++++++++++------
shortest_path_watershed.hh | 76 +++++++++++++++++----------------------------
2 files changed, 49 insertions(+), 56 deletions(-)
Index: levillain/morpho/lower_completion.hh
--- levillain/morpho/lower_completion.hh (revision 0)
+++ levillain/morpho/lower_completion.hh (working copy)
@@ -1,4 +1,4 @@
-// Copyright (C) 2005 EPITA Research and Development Laboratory
+// Copyright (C) 2005, 2008 EPITA Research and Development Laboratory (LRDE)
//
// This file is part of the Olena Library. This library is free
// software; you can redistribute it and/or modify it under the terms
@@ -25,15 +25,26 @@
// reasons why the executable file might be covered by the GNU General
// Public License.
-#ifndef OLENA_MORPHO_LOWER_COMPLETION_HH
-# define OLENA_MORPHO_LOWER_COMPLETION_HH
+#ifndef MLN_MORPHO_LOWER_COMPLETION_HH
+# define MLN_MORPHO_LOWER_COMPLETION_HH
+
+/* FIXME: This file comes from Olena proto-1.0, and needs some
+ adjustments.
+
+ Translate it into the Milena dialect, e.g.,
+ - replace `oln_' and `oln::' by `mln_' and `mln::';
+ - replace `internal::' by `impl::';
+ - adjust the names of interfaces and types;
+ - adjust other names;
+ - adjust calling conventions;
+ - adjust contracts (static/dynamic pre-/postconditions, etc.);
+ - etc. */
# include <queue>
-# include <oln/basics.hh>
-# include <oln/level/fill.hh>
+# include <mln/level/fill.hh>
-namespace oln {
+namespace mln {
namespace morpho {
@@ -133,8 +144,8 @@
return output;
}
- } // end of namespace oln::morpho
+ } // end of namespace mln::morpho
-} // end of namespace oln
+} // end of namespace mln
-#endif // ! OLENA_MORPHO_LOWER_COMPLETION_HH
+#endif // ! MLN_MORPHO_LOWER_COMPLETION_HH
Index: levillain/morpho/shortest_path_watershed.hh
--- levillain/morpho/shortest_path_watershed.hh (revision 0)
+++ levillain/morpho/shortest_path_watershed.hh (working copy)
@@ -1,4 +1,4 @@
-// Copyright (C) 2005 EPITA Research and Development Laboratory
+// Copyright (C) 2005, 2008 EPITA Research and Development Laboratory (LRDE)
//
// This file is part of the Olena Library. This library is free
// software; you can redistribute it and/or modify it under the terms
@@ -25,52 +25,34 @@
// reasons why the executable file might be covered by the GNU General
// Public License.
-#ifndef OLENA_MORPHO_SHORTEST_PATH_WATERSHED_HH
-# define OLENA_MORPHO_SHORTEST_PATH_WATERSHED_HH
+#ifndef MLN_MORPHO_SHORTEST_PATH_WATERSHED_HH
+# define MLN_MORPHO_SHORTEST_PATH_WATERSHED_HH
+
+/* FIXME: This file comes from Olena proto-1.0, and needs some
+ adjustments.
+
+ Translate it into the Milena dialect, e.g.,
+ - replace `oln_' and `oln::' by `mln_' and `mln::';
+ - replace `internal::' by `impl::';
+ - adjust the names of interfaces and types;
+ - adjust other names;
+ - adjust calling conventions;
+ - adjust contracts (static/dynamic pre-/postconditions, etc.);
+ - etc. */
# include <queue>
-# include "oln/level/fill.hh"
-# include "oln/level/extrema_components.hh"
-# include "oln/morpho/lower_completion.hh"
+# include <mln/level/fill.hh>
+# include <mln/level/extrema_components.hh>
+# include <mln/morpho/lower_completion.hh>
+# include <mln/trait/value_.hh>
-namespace oln {
+namespace mln {
namespace morpho {
namespace internal {
- /// Functor used in ordered queues of points.
- template <typename I>
- class greater_point_type
- {
- public:
- typedef oln_type_of(I, point) point_type;
-
- greater_point_type(const abstract::image<I>& im) :
- im_ (im)
- {
- }
-
- /// Is \a x greater than \a y?
- bool operator()(const point_type& x, const point_type& y)
- {
- return im_[x] > im_[y];
- }
-
- private:
- const abstract::image<I>& im_;
- };
-
- /// Facade to build a oln::level::greater_point_type.
- template <typename I>
- greater_point_type<I>
- greater_point(const abstract::image<I>& im)
- {
- return greater_point_type<I>(im);
- }
-
-
// FIXME: To be rewritten. Moreover, the distance d(p, q) is not
// taken into account in this version of cost (but it should not
// bother us as long as we are using first-order neighborhoods).
@@ -107,16 +89,16 @@
typename ch_value_type<I, DestValue>::ret
shortest_path_watershed_(const abstract::image_with_nbh<I>& input)
{
- mlc_is_a(I, abstract::scalar_valued_image)::ensure();
+ // FIXME: Ensure the input image has scalar values.
- const DestValue wshed = ntg_min_val(DestValue);
+ const DestValue wshed = min_val(DestValue);
// We keep a track of already processed points.
typename ch_value_type<I, bool>::ret processed (input.size(),
input.nbh_get());
level::fill (processed, false);
- // Initialise output with the minima components.
+ // Initialize output with the minima components.
typename ch_value_type<I, DestValue>::ret output =
level::minima_components<DestValue>(input);
@@ -139,9 +121,9 @@
typedef
std::priority_queue<point_type,
std::vector<point_type>,
- internal::greater_point_type<dist_type> >
+ util::greater_point<dist_type> >
ordered_queue_type;
- ordered_queue_type q(internal::greater_point(dist));
+ ordered_queue_type q(util::make_greater_point(dist));
// Fill the ordered queue with the points of the border of the
// minima of the (lower complete) input.
for_all_p (p)
@@ -197,7 +179,7 @@
return output;
}
- } // End of namespace oln::morpho::internal.
+ } // End of namespace mln::morpho::internal.
/*! Watershed transform w.r.t. topographical distance based on
@@ -231,8 +213,8 @@
return internal::shortest_path_watershed_<DestValue>(input);
}
- } // end of namespace oln::morpho
+ } // end of namespace mln::morpho
-} // end of namespace oln
+} // end of namespace mln
-#endif // ! OLENA_MORPHO_SHORTEST_PATH_WATERSHED_HH
+#endif // ! MLN_MORPHO_SHORTEST_PATH_WATERSHED_HH