* apps/morphers/recorder-bft.cc: New. * apps/morphers/image2d-skel.hh: New. * apps/morphers/Makefile.am (noinst_PROGRAMS): Add recorder-bft. (recorder_wst_SOURCES): New. (recorder_SOURCES, recorder_wst_SOURCES, mask_recorder_SOURCES): Move recorder.hh... (noinst_HEADERS): ...here (new variable). (MOSTLYCLEANFILES): Add lena-bft??????.pbm. --- milena/ChangeLog | 13 +++ milena/apps/morphers/Makefile.am | 11 ++- milena/apps/morphers/image2d-skel.hh | 137 ++++++++++++++++++++++++++++++++++ milena/apps/morphers/recorder-bft.cc | 114 ++++++++++++++++++++++++++++ 4 files changed, 272 insertions(+), 3 deletions(-) create mode 100644 milena/apps/morphers/image2d-skel.hh create mode 100644 milena/apps/morphers/recorder-bft.cc
diff --git a/milena/ChangeLog b/milena/ChangeLog index 2b98497..5d791e6 100644 --- a/milena/ChangeLog +++ b/milena/ChangeLog @@ -1,3 +1,16 @@ +2011-11-28 Roland Levillain roland@lrde.epita.fr + + New morpher example: recording a breadth-first thinning. + + * apps/morphers/recorder-bft.cc: New. + * apps/morphers/image2d-skel.hh: New. + * apps/morphers/Makefile.am (noinst_PROGRAMS): Add recorder-bft. + (recorder_wst_SOURCES): New. + (recorder_SOURCES, recorder_wst_SOURCES, mask_recorder_SOURCES): + Move recorder.hh... + (noinst_HEADERS): ...here (new variable). + (MOSTLYCLEANFILES): Add lena-bft??????.pbm. + 2011-11-24 Roland Levillain roland@lrde.epita.fr
New morpher example: recording a watershed transform. diff --git a/milena/apps/morphers/Makefile.am b/milena/apps/morphers/Makefile.am index a3b9a41..8633933 100644 --- a/milena/apps/morphers/Makefile.am +++ b/milena/apps/morphers/Makefile.am @@ -25,17 +25,22 @@ AM_CXXFLAGS = $(APPS_CXXFLAGS) noinst_PROGRAMS = \ mask+channel \ recorder \ + recorder-bft \ recorder-wst \ mask+recorder
+noinst_HEADERS = recorder.hh + mask_channel_SOURCES = mask+channel.cc -recorder_SOURCES = recorder.cc recorder.hh -recorder_wst_SOURCES = recorder-wst.cc recorder.hh -mask_recorder_SOURCES = mask+recorder.cc recorder.hh +recorder_SOURCES = recorder.cc +recorder_bft_SOURCES = recorder-bft.cc image2d-skel.hh +recorder_wst_SOURCES = recorder-wst.cc +mask_recorder_SOURCES = mask+recorder.cc
MOSTLYCLEANFILES = \ lena-mask-channel.ppm \ lena-fill??????.ppm \ + lena-bft??????.pbm \ lena-wst??????.ppm \ lena-roi-fill??????.ppm
diff --git a/milena/apps/morphers/image2d-skel.hh b/milena/apps/morphers/image2d-skel.hh new file mode 100644 index 0000000..392122c --- /dev/null +++ b/milena/apps/morphers/image2d-skel.hh @@ -0,0 +1,137 @@ +// Copyright (C) 2010, 2011 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 http://www.gnu.org/licenses/. +// +// 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 APPS_MORPHERS_IMAGE2D_SKEL_HH +# define APPS_MORPHERS_IMAGE2D_SKEL_HH + +/// \file +/// \brief Definitions for a simplicity criterion and a constraint +/// to be used in the breadth-first thinning of a 2D regular image. + +/* FIXME: Remove this file as soon as patches from the mesh-segm-skel + branch have been merged into the current branch. */ + +# include <mln/topo/is_simple_2d.hh> + + +/** \brief An equivalent (for mln::image2d) of the + mln::topo::is_simple_cell functor, based on the mask-based + criterion mln::topo::is_simple_2d. + + This functor acts as an adapter, since mln::topo::is_simple_2d_t + does not fit (yet) in the canvas of + mln::topo::skeleton::breadth_first_thinning. */ +template <typename I, typename N> +class is_simple_2d : public mln::Function_v2b< is_simple_2d<I, N> >, + private mln::topo::is_simple_2d_t<N> +{ +public: + typedef mln::topo::is_simple_2d_t<N> super; + + /// Result type of the functor. + typedef bool result; + + /// Build a functor. + /// + /// \param nbh_fg The foreground neighborhood. + /// \param nbh_bg The background neighborhood. + is_simple_2d(const mln::Neighborhood<N>& nbh_fg) + : super(mln::exact(nbh_fg)), + ima_(0) + { + } + + /// Build a functor, and assign an image to it. + /// + /// \param nbh_fg The foreground neighborhood. + /// \param nbh_bg The background neighborhood. + /// \apram ima The image. + is_simple_2d(const mln::Neighborhood<N>& nbh_fg, + const mln::Image<I>& ima) + : super(mln::exact(nbh_fg)), + ima_(mln::exact(&ima)) + { + } + + /// Set the underlying image. + void set_image(const mln::Image<I>& ima) + { + ima_ = mln::exact(&ima); + } + + /// Based on connectivity numbers. + bool operator()(const mln_psite(I)& p) const + { + return check(*ima_, p); + } + +private: + /// The image. + const I* ima_; +}; + + +template <typename I> +void +detach(const mln_psite(I)& p, mln::Image<I>& ima) +{ + mln::exact(ima)(p) = false; +} + + +template <typename I, typename N> +struct is_not_end_point : public mln::Function_v2b< is_not_end_point<I, N> > +{ + /// Build a functor, and assign an image to it. + /// + /// \param nbh_fg The foreground neighborhood. + /// \apram ima The image. + is_not_end_point(const mln::Neighborhood<N>& nbh, + const mln::Image<I>& ima) + : nbh_(mln::exact(nbh)), + ima_(mln::exact(ima)) + { + } + + // Is \a p not a end point? + bool operator()(const mln_psite(I)& p) const + { + // Number of foreground neighbors pixels. + unsigned nneighbs = 0; + mln_niter(N) n(nbh_, p); + for_all(n) + if (ima_.has(n) && ima_(n)) + ++nneighbs; + return nneighbs != 1; + } + +private: + /// The foreground neighborhood. + const N& nbh_; + /// The image. + const I& ima_; +}; + +#endif // ! APPS_MORPHERS_IMAGE2D_SKEL_HH diff --git a/milena/apps/morphers/recorder-bft.cc b/milena/apps/morphers/recorder-bft.cc new file mode 100644 index 0000000..5ca0e9e --- /dev/null +++ b/milena/apps/morphers/recorder-bft.cc @@ -0,0 +1,114 @@ +// Copyright (C) 2011 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 http://www.gnu.org/licenses/. +// +// 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. + +/// \file +/// \brief Exercise a morpher recording every change in the morphed +/// image with the breadth-first thinning. +/// +/// To produce an AVI movie from the `lena-bft*.pbm' files, use: +/// +/// for f in lena-bft*pbm; convert $f -define png:bit-depth=8 -scale 2500% $(basename $f .pbm).png +/// mencoder "mf://lena-bft*.png" -o lena-bft.avi -ovc lavc -lavcopts gray:vcodec=mjpeg +/// +/// Note: the `-define png:bit-depth=8' option prevents convert from +/// creating an image with 1-bit channels (since the image is binary) +/// and forces it to use 8-bit channels. Indeed mencoder is unable to +/// process PNG images with 1-bit channel(s). +/// +/// The output `lena-bft.avi' can be embedded in a PDF file. */ + +/* FIXME: Modernize this program as soon as patches from the + mesh-segm-skel branch have been merged into the current branch. */ + +#include <string> + +#include <mln/core/image/image2d.hh> +#include <mln/core/alias/neighb2d.hh> + +#include <mln/win/multiple_size.hh> +#include <mln/make/dual_neighb.hh> + +#include <mln/data/compare.hh> + +#include <mln/topo/skeleton/breadth_first_thinning.hh> + +#include <mln/test/predicate.hh> +#include <mln/pw/value.hh> + +#include <mln/labeling/colorize.hh> + +#include <mln/io/pbm/load.hh> +#include <mln/io/pbm/save.hh> + +#include "apps/morphers/recorder.hh" +/* FIXME: Temporarily reuse an old file from apps/generic-skel instead + of newer files integrated into the library (but not merged in). */ +#include "apps/morphers/image2d-skel.hh" + +#include "apps/data.hh" + + +int main() +{ + using namespace mln; + + typedef image2d<bool> I; + I lena = io::pbm::load(MLN_IMG_DIR "/tiny.pbm"); + // Attach recorder to input image. + typedef decorated_image< I, recorder<I> > J; + J lena_rec = record(lena); + + // Dual neighborhood. + typedef neighb< win::multiple_size<2, window2d, pw::value_<I> > > N; + N nbh = make::dual_neighb(lena, c4(), c8()); + // Simplicity criterion functor. + ::is_simple_2d<J, N> is_simple(nbh); + // Constraint. + ::is_not_end_point<J, neighb2d> constraint(c4(), lena_rec); + + J bft_rec = topo::skeleton::breadth_first_thinning(lena_rec, nbh, + is_simple, ::detach<J>, + constraint); + + // Dump recorded frames. + /* FIXME: Hand-made (it partly duplicates ppm::save() (or more + precisely pbm::save()) for decorated_image's). We should be able + to process sequences of images. */ + bool output_initialized_p = false; + for (size_t i = 0; i < bft_rec.decoration().sequence.size(); ++i) + { + I frame = bft_rec.decoration().sequence[i]; + + // Skip frames until the inital image has been seen. + if (frame == lena) + output_initialized_p = true; + if (!output_initialized_p) + continue; + + std::stringstream s; + s << std::setfill ('0') << std::setw (6) << i; + io::pbm::save(frame, std::string("lena-bft") + s.str() + ".pbm"); + } +}