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

Import the code of the illustrations of the ISMM 2009 paper.

	* apps/papers/,
	* apps/papers/levillain.09.ismm/:
	New directories.
	* apps/papers/levillain.09.ismm/chain.hh,
	* apps/papers/levillain.09.ismm/complex.cc,
	* apps/papers/levillain.09.ismm/graph.cc,
	* apps/papers/levillain.09.ismm/image2d.cc:
	New (imported from ISMM 2009).
	Adjust.
	* apps/Makefile.am (SUBDIRS): Add papers.
	* apps/papers/Makefile.am,
	* apps/papers/levillain.09.ismm/Makefile.am:
	New.

git-svn-id: https://svn.lrde.epita.fr/svn/oln/trunk@4690 4aad255d-cdde-0310-9447-f3009e2ae8c0
parent 99c05d51
2009-10-02 Roland Levillain <roland@lrde.epita.fr>
Import the code of the illustrations of the ISMM 2009 paper.
* apps/papers/,
* apps/papers/levillain.09.ismm/:
New directories.
* apps/papers/levillain.09.ismm/chain.hh,
* apps/papers/levillain.09.ismm/complex.cc,
* apps/papers/levillain.09.ismm/graph.cc,
* apps/papers/levillain.09.ismm/image2d.cc:
New (imported from ISMM 2009).
Adjust.
* apps/Makefile.am (SUBDIRS): Add papers.
* apps/papers/Makefile.am,
* apps/papers/levillain.09.ismm/Makefile.am:
New.
2009-10-28 Guillaume Lazzara <z@lrde.epita.fr>
 
Small fixes.
......@@ -15,6 +15,8 @@
# along with Olena. If not, see <http://www.gnu.org/licenses/>.
#
## Process this file through Automake to produce Makefile.in.
# Applied examples of Milena.
SUBDIRS = mesh-segm-skel graph-morpho constrained-connectivity
# Examples from papers.
SUBDIRS += papers
# Copyright (C) 2008, 2009 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/>.
# Examples from papers.
SUBDIRS = levillain.09.ismm
# Copyright (C) 2008, 2009 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 from the ISMM 2009 paper.
# 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_HEADERS = chain.hh
noinst_PROGRAMS = image2d graph complex
image2d_SOURCES = image2d.cc
graph_SOURCES = graph.cc
complex_SOURCES = complex.cc
// Copyright (C) 2009 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 CHAIN_HH
# define CHAIN_HH
#include <mln/morpho/closing/area.hh>
#include <mln/morpho/watershed/flooding.hh>
template <typename I, typename N, typename L>
mln_ch_value(I, L)
chain(const mln::Image<I>& ima, const mln::Neighborhood<N>& nbh,
unsigned lambda, L& nbasins)
{
mln_concrete(I) c = mln::morpho::closing::area(ima, nbh, lambda);
mln_ch_value(I, L) s = mln::morpho::watershed::flooding(c, nbh, nbasins);
return s;
}
#endif // ! CHAIN_HH
// Copyright (C) 2009 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.
#include <cstdlib>
#include <string>
#include <mln/core/image/complex_image.hh>
#include <mln/core/image/complex_neighborhoods.hh>
#include <mln/core/image/complex_windows.hh>
#include <mln/value/int_u8.hh>
#include <mln/value/label_16.hh>
#include <mln/data/fill.hh>
#include <mln/math/diff_abs.hh>
#include <mln/literal/zero.hh>
#include <mln/io/off/load.hh>
#include <mln/io/off/save.hh>
#include <mln/labeling/colorize.hh>
#include "chain.hh"
// FIXME: Should be rewritten using a diff_abs-based accumulator
// taking values from triangles (and checking that exactly two values
// where taken, of course).
template <unsigned D, typename G, typename V>
inline
mln::complex_image<D, G, V>
gradient_on_edges(const mln::complex_image<D, G, V>& input)
{
typedef mln::complex_image<D, G, V> ima_t;
ima_t output (input.domain());
mln::data::fill(output, mln::literal::zero);
// Values on edges.
mln::p_n_faces_fwd_piter<D, G> e(input.domain(), 1);
// Edge-to-triangle adjacency.
typedef mln::complex_higher_neighborhood<D, G> e2t_t;
e2t_t e2t;
mln_niter(e2t_t) t(e2t, e);
// Iterate on edges (1-faces).
for_all(e)
{
t.start();
// An edge should be adjacent to at least one triangle.
if (!t.is_valid())
abort();
V v1 = input(t);
t.next();
// If E is adjacent to two triangles, compute the absolute
// difference between their values.
if (t.is_valid())
{
V v2 = input(t);
output(e) = mln::math::diff_abs(v1, v2);
// There should be no more adjacent triangles.
t.next();
mln_assertion(!t.is_valid());
}
}
return output;
}
int main(int argc, char* argv[])
{
if (argc != 4)
{
std::cerr << "usage: " << argv[0] << " input.off lambda output.off"
<< std::endl;
std::exit(1);
}
std::string input_filename = argv[1];
unsigned lambda = atoi(argv[2]);
std::string output_filename = argv[3];
using namespace mln;
typedef float val;
typedef value::label_16 label;
// Input and output types.
typedef mln::float_2complex_image3df input;
typedef mln_ch_value_(input, label) output;
static const unsigned dim = input::dim;
typedef mln_geom_(input) geom;
complex_lower_dim_connected_n_face_neighborhood<dim, geom> nbh;
complex_lower_dim_connected_n_face_window_p<dim, geom> win;
label nbasins;
// Load, process, save.
/* FIXME: The domain of IMA should be limited to 2-faces. Alas, we
do not have (yet) an mln::pcomplex_subset site set type. Anyway,
this should not alter the results, only slow the computation
down. */
input ima;
io::off::load(ima, input_filename);
// Gradient on edges.
input g = gradient_on_edges(ima);
output s = chain(ima, nbh, lambda, nbasins);
io::off::save(labeling::colorize(value::rgb8(), s, nbasins),
output_filename);
}
// Copyright (C) 2008, 2009 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.
#include <iostream>
#include <iomanip>
#include <vector>
#include <mln/core/image/image2d.hh>
#include <mln/core/routine/duplicate.hh>
#include <mln/core/alias/neighb2d.hh>
#include <mln/core/site_set/p_queue_fast.hh>
#include <mln/labeling/blobs.hh>
#include <mln/io/pgm/load.hh>
#include <mln/debug/println.hh>
#include <mln/draw/line.hh>
#include <mln/pw/all.hh>
#include <mln/binarization/threshold.hh>
#include <mln/value/int_u8.hh>
#include <mln/value/label_8.hh>
#include <mln/core/alias/point2d.hh>
#include <mln/core/site_set/p_faces.hh>
#include <mln/core/image/complex_image.hh>
#include <mln/core/alias/complex_geometry.hh>
#include <mln/core/alias/complex_image.hh>
// FIXME: Include these elsewhere? (In complex_image.hh?)
#include <mln/core/image/complex_neighborhoods.hh>
#include <mln/core/image/complex_neighborhood_piter.hh>
#include <mln/core/image/complex_windows.hh>
#include <mln/data/fill.hh>
#include <mln/morpho/gradient.hh>
#include <mln/labeling/colorize.hh>
#include "chain.hh"
// FIXME: Copied and adjusted from pics/graph.cc; factor.
namespace mln
{
template <typename I, typename N>
mln_concrete(I)
influence_zones(const I& input, const N& nbh)
{
mln_concrete(I) output = duplicate(input);
p_queue_fast<mln_site(I)> q;
{
// Initialization.
mln_piter(I) p(input.domain());
mln_niter(N) n(nbh, p);
for_all(p)
if (input(p) != 0)
for_all(n) if (input.has(n))
if (input(n) == 0)
{
q.push(p);
break;
}
}
{
// Body.
mln_site(I) p;
mln_niter(N) n(nbh, p);
while (! q.is_empty())
{
p = q.pop_front();
mln_invariant(output(p) != 0);
for_all(n) if (input.has(n))
if (output(n) == 0)
{
output(n) = output(p);
q.push(n);
}
}
}
return output;
}
namespace io
{
namespace neato
{
/* FIXME: This is just the gray-level version. Handle other value
types as well. */
void
save(const complex_image<1, discrete_plane_1complex_geometry,
value::int_u8>& ima,
const std::string& filename,
const std::string& bgcolor = "#0000C0",
const std::string& fontcolor = "#0000C0",
bool empty_vertex_label = true)
{
typedef value::int_u8 V;
typedef complex_image<1, discrete_plane_1complex_geometry, V> I;
const unsigned D = 1;
typedef discrete_plane_1complex_geometry G;
std::ofstream g(filename.c_str());
g << "graph wst" << std::endl
<< "{" << std::endl
<< " graph [bgcolor = \"" << bgcolor << "\"]" << std::endl
<< " edge [color = \"#FFFFFF\"]" << std::endl
<< " node [color = \"#FFFFFF\", height=\"5\", width=\"5\","
<< " fontsize=\"100\", fontcolor = \"" << fontcolor << "\"]"
<< std::endl;
// Vertices.
p_n_faces_fwd_piter<D, G> v(ima.domain(), 0);
typedef complex_higher_neighborhood<D, G> e_nbh_t;
e_nbh_t e_nbh;
for_all(v)
{
V vertex_color = ima(v);
std::ostringstream vertex_color_str;
// FIXME: Only valid for gray-level images.
vertex_color_str << '#'
<< std::hex
<< std::setfill('0')
<< std::setw(2) << vertex_color
<< std::setw(2) << vertex_color
<< std::setw(2) << vertex_color
<< std::dec;
g << " v" << v.unproxy_().face_id()
<< " [pos = \""
<< std::fixed << std::setprecision(1)
<< (float)v.to_site().front()[1] << ", "
<< -(float)v.to_site().front()[0]
<< "\", color = \"" << vertex_color_str.str()
<< "\", fillcolor = \"" << vertex_color_str.str()
<< "\", pin = \"true\", style=\"filled,setlinewidth(3)\"";
if (empty_vertex_label)
g << ", label = \"\"";
g << "];"
<< std::endl;
}
// Edges.
p_n_faces_fwd_piter<D, G> e(ima.domain(), 1);
typedef complex_lower_neighborhood<D, G> v_nbh_t;
v_nbh_t v_nbh;
mln_niter_(v_nbh_t) adj_v(v_nbh, e);
for_all(e)
{
V edge_color = ima(e);
std::ostringstream edge_color_str;
edge_color_str << '#'
<< std::hex
<< std::setfill('0')
<< std::setw(2) << edge_color
<< std::setw(2) << edge_color
<< std::setw(2) << edge_color
<< std::dec;
// Adjacent vertices.
adj_v.start();
topo::face<1> v1 = adj_v.unproxy_().face();
point2d p1 = adj_v.to_site().front();
adj_v.next();
topo::face<1> v2 = adj_v.unproxy_().face();
point2d p2 = adj_v.to_site().front();
adj_v.next();
mln_invariant(!adj_v.is_valid());
g << " v" << v1.face_id() << " -- v" << v2.face_id() << " ";
g << "[color = \"" << edge_color_str.str()
<< "\", style=\"setlinewidth(10)\"];" << std::endl;
}
g << "}" << std::endl;
g.close();
}
// FIXME: Factor with the previous version.
void
save(const complex_image<1, discrete_plane_1complex_geometry,
value::rgb8>& ima,
const std::string& filename,
const std::string& bgcolor = "#0000C0",
const std::string& fontcolor = "#0000C0",
bool empty_vertex_label = true)
{
typedef value::rgb8 V;
typedef complex_image<1, discrete_plane_1complex_geometry, V> I;
const unsigned D = 1;
typedef discrete_plane_1complex_geometry G;
std::ofstream g(filename.c_str());
g << "graph wst" << std::endl
<< "{" << std::endl
<< " graph [bgcolor = \"" << bgcolor << "\"]" << std::endl
<< " edge [color = \"#FFFFFF\"]" << std::endl
<< " node [color = \"#FFFFFF\", height=\"5\", width=\"5\","
<< " fontsize=\"100\", fontcolor = \"" << fontcolor << "\"]"
<< std::endl;
// Vertices.
p_n_faces_fwd_piter<D, G> v(ima.domain(), 0);
typedef complex_higher_neighborhood<D, G> e_nbh_t;
e_nbh_t e_nbh;
for_all(v)
{
V vertex_color = ima(v);
std::ostringstream vertex_color_str;
// FIXME: Only valid for gray-level images.
vertex_color_str << '#'
<< std::hex
<< std::setfill('0')
<< std::setw(2) << vertex_color.red()
<< std::setw(2) << vertex_color.green()
<< std::setw(2) << vertex_color.blue()
<< std::dec;
g << " v" << v.unproxy_().face_id()
<< " [pos = \""
<< std::fixed << std::setprecision(1)
<< (float)v.to_site().front()[1] << ", "
<< -(float)v.to_site().front()[0]
<< "\", color = \"" << vertex_color_str.str()
<< "\", fillcolor = \"" << vertex_color_str.str()
<< "\", pin = \"true\", style=\"filled,setlinewidth(3)\"";
if (empty_vertex_label)
g << ", label = \"\"";
g << "];"
<< std::endl;
}
// Edges.
p_n_faces_fwd_piter<D, G> e(ima.domain(), 1);
typedef complex_lower_neighborhood<D, G> v_nbh_t;
v_nbh_t v_nbh;
mln_niter_(v_nbh_t) adj_v(v_nbh, e);
for_all(e)
{
V edge_color = ima(e);
std::ostringstream edge_color_str;
edge_color_str << '#'
<< std::hex
<< std::setfill('0')
<< std::setw(2) << edge_color.red()
<< std::setw(2) << edge_color.green()
<< std::setw(2) << edge_color.blue()
<< std::dec;
// Adjacent vertices.
adj_v.start();
topo::face<1> v1 = adj_v.unproxy_().face();
point2d p1 = adj_v.to_site().front();
adj_v.next();
topo::face<1> v2 = adj_v.unproxy_().face();
point2d p2 = adj_v.to_site().front();
adj_v.next();
mln_invariant(!adj_v.is_valid());
g << " v" << v1.face_id() << " -- v" << v2.face_id() << " ";
g << "[color = \"" << edge_color_str.str()
<< "\", style=\"setlinewidth(10)\"];" << std::endl;
}
g << "}" << std::endl;
g.close();
}
} // end of namespace mln::io::neato
} // end of namespace mln::io
} // end of namespace mln
// FIXME: Clean up and move into Milena?
mln::int_u8_1complex_image2d
make_complex_image(const mln::image2d<mln::value::int_u8>& input)
{
using namespace mln;
using mln::value::int_u8;
/*----------------------------------------.
| Complex + complex geometry (location). |
`----------------------------------------*/
border::thickness = 0;
unsigned nlabels;
image2d<unsigned> label =
labeling::blobs(mln::binarization::threshold(input, 1), c4(), nlabels);
std::cout << "n seeds = " << nlabels << std::endl;
{
image2d<int_u8> lab(label.domain());
data::paste(label, lab);
}
image2d<unsigned> iz = influence_zones(label, c4());
{
image2d<int_u8> IZ(iz.domain());
data::paste(iz, IZ);
}
// Make graph/complex.
std::vector< std::vector<bool> > adj(nlabels + 1);
for (unsigned l = 1; l <= nlabels; ++l)
adj[l].resize(nlabels + 1, false);
{
box2d::piter p(iz.domain());
for_all(p)
{