Commit cffe62e5 authored by Roland Levillain's avatar Roland Levillain
Browse files

Merge branch 'apps-morphers' into next

Conflicts:
	ChangeLog
	milena/ChangeLog
parents b654f0d4 13174eaf
2011-11-14 Roland Levillain <roland@lrde.epita.fr>
* configure.ac: Configure milena/apps/morphers/Makefile.
2011-11-16 Guillaume Lazzara <z@lrde.epita.fr>
* m4/oln-with-lib.m4: Lookup for multiple AND single libraries
......
......@@ -566,6 +566,7 @@ AC_CONFIG_FILES([
milena/apps/constrained-connectivity/Makefile
milena/apps/graph-morpho/Makefile
milena/apps/mesh-segm-skel/Makefile
milena/apps/morphers/Makefile
milena/apps/papers/Makefile
milena/apps/papers/levillain.09.ismm/Makefile
])
......
2012-09-25 Roland Levillain <roland@lrde.epita.fr>
Fix file names in apps/morphers/recorder-bft.
* apps/morphers/recorder-bft.cc,
* apps/morphers/Makefile.am (recorder_SOURCES):
s/lena/picasso/.
2012-06-21 Roland Levillain <roland@lrde.epita.fr>
Improve the (movie) output of apps/morphers/iz.
* apps/morphers/iz.cc (save_colorized): Skip consecutive identical
frames.
Swap the frame reconstruction and write operations, so that the
last frame is eventually saved.
* apps/morphers/Makefile.am (tmp_iz.avi): Use a speedup of 50
instead of 10.
2012-06-21 Roland Levillain <roland@lrde.epita.fr>
Use the lazy recorder morpher in apps/morphers/iz.
* apps/morphers/iz.cc (main): Here.
Save the sequence of recorder images as PNG images using Magick++.
(save_colorized): New function.
* apps/morphers/Makefile.am
(noinst_PROGRAMS, iz_SOURCES) [HAVE_MAGICKXX]: Build `iz' only if
Magick++ is available.
(iz_output.ppm) [HAVE_MAGICKXX]: Likewise.
(iz_CPPFLAGS, iz_LDFLAGS) [HAVE_MAGICKXX]: New.
(tmp_iz.avi) [HAVE_MAGICKXX]: New target.
(MOSTLYCLEANFILES): Add tmp_iz??????.png.
2012-06-20 Roland Levillain <roland@lrde.epita.fr>
Add rules to build and run apps/morphers/iz.
* apps/morphers/Makefile.am (noinst_PROGRAMS): Add iz.
(iz_SOURCES): New.
(EXTRA_DIST): Add iz_input.pbm.
(iz_output.ppm): New target.
(MOSTLYCLEANFILES): Add tmp_clo.pgm, tmp_dmap.pgm,
tmp_ws_superpose.ppm, tmp_ws.ppm, tmp_lab.pgm, tmp_iz.pgm,
tmp_iz.ppm and tmp_iz_input.ppm.
2012-06-20 Thierry Geraud <thierry.geraud@lrde.epita.fr>
New example demonstrating the use of influence zones.
* apps/morphers/iz.cc: New.
* apps/morphers/iz_input.pbm: New.
2012-06-21 Roland Levillain <roland@lrde.epita.fr>
New ``lazy'' recorder morpher in apps/morpher.
* apps/morphers/lazy_recorder.hh: New.
* apps/morphers/lazy_recorder.cc: New test.
* apps/morphers/Makefile.am (noinst_HEADERS):
Add lazy_recorder.hh.
(noinst_PROGRAMS): Add lazy_recorder.
(lazy_recorder_SOURCES): New.
(MOSTLYCLEANFILES): Add lena-fill-lazy??????.ppm.
2012-06-21 Roland Levillain <roland@lrde.epita.fr>
Render the recorder morpher more generic.
* apps/morphers/recorder.hh (recorder<I>::sequence): Store a
sequence of concrete images.
Add missing inline keywords.
Improve the documentation.
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.
* apps/morphers/recorder-wst.cc: New.
* apps/morphers/Makefile.am (noinst_PROGRAMS): Add recorder-wst.
(recorder_wst_SOURCES): New.
(MOSTLYCLEANFILES): lena-wst??????.ppm.
2011-11-24 Roland Levillain <roland@lrde.epita.fr>
Work around decorated_image's lack of properties, for the recorder.
* apps/morphers/recorder.hh
(trait::image_< decorated_image<I,D> >): Replace with..
(trait::image_< decorated_image<I, recorder<I> > >): ...this.
Add a value_storage property.
(ch_value_< decorated_image< image_<I>, data_< recorder<I> > >, V >):
New trait, to handle changes of value types in images decorated
with a recorder.
* apps/morphers/mask+channel.cc,
* apps/morphers/mask+recorder.cc,
* apps/morphers/recorder.cc:
Simplify these programs thanks to the preceding changes.
2011-11-24 Roland Levillain <roland@lrde.epita.fr>
Fix the initialization of mln::decorated_image.
* mln/tag/init.hh (tag::data_t): New tag type.
(tag::data): New tag.
* mln/core/image/imorph/decorated_image.hh
(init_(tag::data_t, D&, const decorated_image<I,D>&))
(init_(tag::data_t, D&, const decorated_image<I,D2>&))
(init_(tag::image_t, decorated_image<I,D>&, const J&)):
New functions.
2011-11-18 Roland Levillain <roland@lrde.epita.fr>
Simplify some apps/morpher programs using mln::make::box2d.
* apps/morphers/mask+recorder.cc,
* apps/morphers/mask+channel.cc:
Here.
2011-11-18 Roland Levillain <roland@lrde.epita.fr>
Refactor apps/morphers.
* apps/morphers/recorder.cc,
* apps/morphers/mask+recorder.cc:
Factor common parts...
* apps/morphers/recorder.hh: ...here (new file).
* apps/morphers/Makefile.am
(recorder_SOURCES, mask_recorder_SOURCES): Add recorder.hh.
2011-11-14 Roland Levillain <roland@lrde.epita.fr>
Add examples of morphers.
* apps/morphers/mask+channel.cc,
* apps/morphers/recorder.cc,
* apps/morphers/mask+recorder.cc:
New.
* apps/morphers/Makefile.am: New.
* apps/Makefile.am (SUBDIRS): Add morphers.
2011-11-24 Roland Levillain <roland@lrde.epita.fr>
 
Honor a precondition in classification examples (ISMM 2009).
# Copyright (C) 2008, 2009 EPITA Research and Development Laboratory (LRDE).
# Copyright (C) 2008, 2009, 2011 EPITA Research and Development
# Laboratory (LRDE).
#
# This file is part of Olena.
#
......@@ -16,7 +17,7 @@
#
# Applied examples of Milena.
SUBDIRS = mesh-segm-skel graph-morpho constrained-connectivity
SUBDIRS = mesh-segm-skel graph-morpho constrained-connectivity morphers
# Examples from papers.
SUBDIRS += papers
# Copyright (C) 2011, 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 <http://www.gnu.org/licenses/>.
# Illustrations of morphers.
# Find Milena headers.
AM_CPPFLAGS = -I$(top_srcdir)/milena -I$(top_builddir)/milena
# Produce fast code.
APPS_CXXFLAGS = @APPS_CXXFLAGS@
AM_CXXFLAGS = $(APPS_CXXFLAGS)
noinst_PROGRAMS = \
mask+channel \
recorder \
recorder-bft \
recorder-wst \
mask+recorder \
lazy_recorder
noinst_HEADERS = recorder.hh lazy_recorder.hh
mask_channel_SOURCES = mask+channel.cc
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
lazy_recorder_SOURCES = lazy_recorder.cc
EXTRA_DIST = iz_input.pbm
if HAVE_MAGICKXX
noinst_PROGRAMS += iz
iz_SOURCES = iz.cc
iz_CPPFLAGS = $(AM_CPPFLAGS) $(MAGICKXX_CPPFLAGS)
iz_LDFLAGS = $(AM_LDFLAGS) $(MAGICKXX_LDFLAGS)
# This target is phony: `iz' never writes `iz_output.ppm' actually.
iz_output.ppm: iz$(EXEEXT) iz_input.pbm
./$< $(srcdir)/iz_input.pbm $@
# Likewise, the dependency of this target is a (wrong) shortcut.
tmp_iz.avi: iz_output.ppm
mencoder "mf://tmp_iz*.png" -o tmp_iz.avi -ovc lavc -lavcopts vcodec=mjpeg -speed 50
endif HAVE_MAGICKXX
MOSTLYCLEANFILES = \
lena-mask-channel.ppm \
lena-fill??????.ppm \
lena-fill-lazy??????.ppm \
picasso-bft??????.pbm \
lena-wst??????.ppm \
lena-roi-fill??????.ppm \
tmp_clo.pgm \
tmp_dmap.pgm \
tmp_ws_superpose.ppm \
tmp_ws.ppm \
tmp_lab.pgm \
tmp_iz.pgm \
tmp_iz??????.png \
tmp_iz.ppm \
tmp_iz_input.ppm
# FIXME: Also produce movies (see comments in recorder.cc and
# mask+recorder.cc.)
// 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
#include <mln/core/image/image2d.hh>
#include <mln/core/alias/neighb2d.hh>
#include <mln/data/fill.hh>
#include <mln/data/paste.hh>
#include <mln/data/convert.hh>
#include <mln/data/saturate.hh>
#include <mln/labeling/value.hh>
#include <mln/labeling/colorize.hh>
#include <mln/morpho/watershed/superpose.hh>
#include <mln/morpho/watershed/flooding.hh>
#include <mln/morpho/elementary/dilation.hh>
#include <mln/morpho/closing/area.hh>
#include <mln/morpho/closing/structural.hh>
#include <mln/transform/distance_front.hh>
#include <mln/transform/influence_zone_front.hh>
#include <mln/pw/all.hh>
#include <mln/core/image/dmorph/image_if.hh>
#include <mln/core/image/dmorph/sub_image.hh>
#include <mln/make/w_window2d.hh>
#include "apps/morphers/lazy_recorder.hh"
#include <mln/io/pbm/load.hh>
#include <mln/io/pbm/save.hh>
#include <mln/io/pgm/save.hh>
#include <mln/io/ppm/save.hh>
#include <mln/io/magick/save.hh>
#include <mln/literal/colors.hh>
// Save as PNG directly, instead of using PPM as intermediate format.
template <typename I>
inline
void
save_colorized(const mln::decorated_image< I, lazy_recorder<I> >& rec,
const std::string& prefix)
{
mln_concrete(I) frame = mln::duplicate(rec.decoration().initial);
std::stringstream s;
s << std::setfill ('0') << std::setw (6) << 0;
mln::io::magick::save(mln::labeling::colorize(mln::value::rgb8(), frame),
prefix + s.str() + ".png");
for (size_t i = 0; i < rec.decoration().sequence.size(); ++i)
{
// Next change (`frame(p)' is assigned the value `v' in the next
// frame).
mln_psite(I) p = rec.decoration().sequence[i].first;
mln_value(I) v = rec.decoration().sequence[i].second;
// Skip consecutive identical frames.
if (frame(p) == v)
continue;
frame(p) = v;
std::stringstream s;
s << std::setfill ('0') << std::setw (6) << i + 1;
mln::io::magick::save(mln::labeling::colorize(mln::value::rgb8(), frame),
prefix + s.str() + ".png");
}
}
void usage(char* argv[])
{
std::cerr << "usage: " << argv[0] << " input.pbm output.ppm" << std::endl;
std::abort();
}
int main(int argc, char* argv[])
{
// Initialize Magick++.
Magick::InitializeMagick(*argv);
using namespace mln;
using value::int_u8;
if (argc != 3)
usage(argv);
image2d<bool> input, clo;
io::pbm::load(input, argv[1]);
/// Structural closing.
clo = morpho::closing::structural(input,
win::rectangle2d(5, 17));
io::pbm::save(clo, "tmp_clo.pgm");
/// Distance map computation.
int ww[] = { 00, 11, 0, 11, 0,
11, 7, 5, 7, 11,
00, 5, 0, 5, 0,
11, 7, 5, 7, 11,
00, 11, 0, 11, 0 };
image2d<int_u8> dmap;
dmap = transform::distance_front(clo,
c4(), make::w_window2d(ww),
mln_max(int_u8));
dmap = morpho::closing::area(dmap, c4(), 500);
io::pgm::save(dmap, "tmp_dmap.pgm");
unsigned nbasins;
image2d<unsigned> ws = morpho::watershed::flooding(dmap,
c4(),
nbasins);
io::ppm::save(labeling::colorize(value::rgb8(), ws), "tmp_ws.ppm");
{
image2d<value::rgb8> ws_ = data::convert(value::rgb8(), input);
data::fill((ws_ | (pw::value(ws) == pw::cst(0))).rw(), literal::red);
io::ppm::save(ws_, "tmp_ws_superpose.ppm");
// test% g++ -I. main.cc -DNDEBUG -O2
// main.cc: In function ‘int main(int, char**)’:
// main.cc:85: error: no matching function for call to ‘convert(mln::image2d<bool>&, mln::value::rgb8)’
// /// Convert the image \p input by changing the value type.
// ///
// /// \param[in] v A value of the destination type.
// /// \param[in] input The input image.
// //
// template <typename V, typename I>
// mln_ch_value(I, V)
// convert(const V& v, const Image<I>& input);
}
image2d<unsigned> lab(input.domain());
data::fill(lab, 0);
unsigned nlabels;
data::paste(labeling::value(ws | make::box2d(0,0,input.nrows()-1,0),
0, c4(), nlabels),
lab);
decorated_image< image2d<unsigned>,
lazy_recorder< image2d<unsigned> > > rec_lab =
lazy_record(lab);
io::pgm::save(data::saturate(value::int_u8(), lab), "tmp_lab.pgm");
image_if< decorated_image< image2d<unsigned int>,
lazy_recorder< image2d<unsigned int> > >,
fun::eq_v2b_expr_< pw::value_< image2d<unsigned int> >,
pw::cst_<int> > > rec_iz =
transform::influence_zone_front(rec_lab | (pw::value(ws) == pw::cst(0)),
c8(),
make::w_window2d(ww));
data::paste(rec_iz, lab);
io::pgm::save(data::saturate(value::int_u8(), lab), "tmp_iz.pgm");
save_colorized(rec_iz.unmorph_(), "tmp_iz");
image2d<value::rgb8> output = labeling::colorize(value::rgb8(), lab);
io::ppm::save(output, "tmp_iz.ppm");
data::fill((output | pw::value(input)).rw(), literal::white);
io::ppm::save(output, "tmp_iz_input.ppm");
}
// Copyright (C) 2011, 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 <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 in a lazy fashion.
///
/// To produce an AVI movie from the `lena-fill*.ppm' files, use:
///
/// for f in lena-fill*ppm; convert $f -scale 2500% $(basename $f .ppm).png
/// mencoder "mf://lena-fill*.png" -o lena-fill.avi -ovc lavc -lavcopts vcodec=mjpeg
///
/// The output `lena-fill.avi' can be embedded in a PDF file. */
#include <string>
#include <mln/core/image/image2d.hh>
#include <mln/value/rgb8.hh>
#include <mln/literal/colors.hh>
#include <mln/data/fill.hh>
#include <mln/io/ppm/load.hh>
#include "apps/morphers/lazy_recorder.hh"
#include "apps/data.hh"