Olena-patches
  Threads by month 
                
            - ----- 2025 -----
 - November
 - October
 - September
 - August
 - July
 - June
 - May
 - April
 - March
 - February
 - January
 - ----- 2024 -----
 - December
 - November
 - October
 - September
 - August
 - July
 - June
 - May
 - April
 - March
 - February
 - January
 - ----- 2023 -----
 - December
 - November
 - October
 - September
 - August
 - July
 - June
 - May
 - April
 - March
 - February
 - January
 - ----- 2022 -----
 - December
 - November
 - October
 - September
 - August
 - July
 - June
 - May
 - April
 - March
 - February
 - January
 - ----- 2021 -----
 - December
 - November
 - October
 - September
 - August
 - July
 - June
 - May
 - April
 - March
 - February
 - January
 - ----- 2020 -----
 - December
 - November
 - October
 - September
 - August
 - July
 - June
 - May
 - April
 - March
 - February
 - January
 - ----- 2019 -----
 - December
 - November
 - October
 - September
 - August
 - July
 - June
 - May
 - April
 - March
 - February
 - January
 - ----- 2018 -----
 - December
 - November
 - October
 - September
 - August
 - July
 - June
 - May
 - April
 - March
 - February
 - January
 - ----- 2017 -----
 - December
 - November
 - October
 - September
 - August
 - July
 - June
 - May
 - April
 - March
 - February
 - January
 - ----- 2016 -----
 - December
 - November
 - October
 - September
 - August
 - July
 - June
 - May
 - April
 - March
 - February
 - January
 - ----- 2015 -----
 - December
 - November
 - October
 - September
 - August
 - July
 - June
 - May
 - April
 - March
 - February
 - January
 - ----- 2014 -----
 - December
 - November
 - October
 - September
 - August
 - July
 - June
 - May
 - April
 - March
 - February
 - January
 - ----- 2013 -----
 - December
 - November
 - October
 - September
 - August
 - July
 - June
 - May
 - April
 - March
 - February
 - January
 - ----- 2012 -----
 - December
 - November
 - October
 - September
 - August
 - July
 - June
 - May
 - April
 - March
 - February
 - January
 - ----- 2011 -----
 - December
 - November
 - October
 - September
 - August
 - July
 - June
 - May
 - April
 - March
 - February
 - January
 - ----- 2010 -----
 - December
 - November
 - October
 - September
 - August
 - July
 - June
 - May
 - April
 - March
 - February
 - January
 - ----- 2009 -----
 - December
 - November
 - October
 - September
 - August
 - July
 - June
 - May
 - April
 - March
 - February
 - January
 - ----- 2008 -----
 - December
 - November
 - October
 - September
 - August
 - July
 - June
 - May
 - April
 - March
 - February
 - January
 - ----- 2007 -----
 - December
 - November
 - October
 - September
 - August
 - July
 - June
 - May
 - April
 - March
 - February
 - January
 - ----- 2006 -----
 - December
 - November
 - October
 - September
 - August
 - July
 - June
 - May
 - April
 - March
 - February
 - January
 - ----- 2005 -----
 - December
 - November
 - October
 - September
 - August
 - July
 - June
 - May
 - April
 - March
 - February
 - January
 - ----- 2004 -----
 - December
 - November
 - October
 - September
 - August
 - July
 - June
 - May
 - April
 - March
 
March 2009
- 9 participants
 - 202 discussions
 
10 Mar '09
                    
                        ---
 ChangeLog    |    4 ++++
 configure.ac |    1 +
 2 files changed, 5 insertions(+), 0 deletions(-)
diff --git a/ChangeLog b/ChangeLog
index 721d35a..e27b4aa 100644
--- a/ChangeLog
+++ b/ChangeLog
@@ -1,3 +1,7 @@
+2009-03-10 Guillaume Lazzara  <z(a)lrde.epita.fr>
+
+	* configure.ac: configure tests/morpho/opening/approx.
+
 2009-03-09  Frederic Bour  <bour(a)lrde.epita.fr>
 
 	Add missing trait for thru_morpher.
diff --git a/configure.ac b/configure.ac
index 40969bb..d8a30b5 100644
--- a/configure.ac
+++ b/configure.ac
@@ -248,6 +248,7 @@ AC_CONFIG_FILES([
       milena/tests/morpho/watershed/Makefile
       milena/tests/morpho/closing/Makefile
       milena/tests/morpho/opening/Makefile
+      milena/tests/morpho/opening/approx/Makefile
     milena/tests/norm/Makefile
     milena/tests/opt/Makefile
     milena/tests/pw/Makefile
-- 
1.5.6.5
                    
                  
                  
                          
                            
                            1
                            
                          
                          
                            
                            0
                            
                          
                          
                            
    
                          
                        
                    
                    
                        	* scribo/Makefile: update include path.
	* scribo/core/central_sites.hh,
	* scribo/core/component_bboxes.hh,
	* scribo/core/erase_bboxes.hh,
	* scribo/core/macros.hh,
	* scribo/debug/save_label_image.hh,
	* scribo/debug/save_linked_textbboxes_image.hh,
	* scribo/debug/save_table_image.hh,
	* scribo/debug/save_textbboxes_image.hh,
	* scribo/draw/bounding_box_links.hh,
	* scribo/draw/bounding_boxes.hh,
	* scribo/make/debug_filename.hh,
	* scribo/make/influence_zone_graph.hh,
	* scribo/table/align_lines_horizontaly.hh,
	* scribo/table/align_lines_verticaly.hh,
	* scribo/table/connect_horizontal_lines.hh,
	* scribo/table/connect_vertical_lines.hh,
	* scribo/table/erase.hh,
	* scribo/table/extract_lines_with_rank.hh,
	* scribo/table/internal/align_lines.hh,
	* scribo/table/internal/connect_lines.hh,
	* scribo/table/internal/repair_lines.hh,
	* scribo/table/repair_horizontal_lines.hh,
	* scribo/table/repair_vertical_lines.hh,
	* scribo/text/extract_bboxes.hh,
	* scribo/text/grouping/group_from_double_link.hh,
	* scribo/text/grouping/group_from_multiple_links.hh,
	* scribo/text/grouping/group_from_single_link.hh,
	* scribo/text/grouping/group_with_multiple_links.hh,
	* scribo/text/grouping/group_with_single_left_link.hh,
	* scribo/text/grouping/group_with_single_right_link.hh,
	* scribo/text/grouping/internal/find_root.hh,
	* scribo/text/grouping/internal/init_link_array.hh,
	* scribo/text/grouping/internal/update_link_array.hh,
	* scribo/text/grouping/internal/update_link_graph.hh,
	* scribo/text/recognition.hh: new routines.
	* scribo/photo.cc,
	* scribo/table.cc: move...
	* scribo/src/photo.cc,
	* scribo/src/table.cc: ... here.
	* scribo/src/extract_text_double_link.cc,
	* scribo/src/extract_text_multiple_links.cc,
	* scribo/src/extract_text_single_link.cc: new small test related to
	text extraction.
---
 milena/sandbox/ChangeLog                           |   52 +++++
 milena/sandbox/scribo/Makefile                     |   23 ++-
 milena/sandbox/scribo/core/central_sites.hh        |   97 ++++++++++
 milena/sandbox/scribo/core/component_bboxes.hh     |   85 +++++++++
 .../scribo/{photo.cc => core/erase_bboxes.hh}      |   77 +++++---
 milena/sandbox/scribo/{photo.cc => core/macros.hh} |   45 +----
 .../scribo/{photo.cc => debug/save_label_image.hh} |   77 +++++----
 .../scribo/debug/save_linked_textbboxes_image.hh   |  170 +++++++++++++++++
 milena/sandbox/scribo/debug/save_table_image.hh    |   97 ++++++++++
 .../sandbox/scribo/debug/save_textbboxes_image.hh  |   90 +++++++++
 milena/sandbox/scribo/draw/bounding_box_links.hh   |  197 ++++++++++++++++++++
 milena/sandbox/scribo/draw/bounding_boxes.hh       |   93 +++++++++
 .../scribo/{photo.cc => make/debug_filename.hh}    |   89 ++++++----
 milena/sandbox/scribo/make/influence_zone_graph.hh |  121 ++++++++++++
 .../sandbox/scribo/src/extract_text_double_link.cc |  109 +++++++++++
 .../scribo/src/extract_text_multiple_links.cc      |   78 ++++++++
 .../sandbox/scribo/src/extract_text_single_link.cc |  108 +++++++++++
 milena/sandbox/scribo/{ => src}/photo.cc           |    0 
 milena/sandbox/scribo/{ => src}/table.cc           |    0 
 .../scribo/table/align_lines_horizontaly.hh        |   97 ++++++++++
 .../sandbox/scribo/table/align_lines_verticaly.hh  |   98 ++++++++++
 .../scribo/table/connect_horizontal_lines.hh       |   94 ++++++++++
 .../sandbox/scribo/table/connect_vertical_lines.hh |   95 ++++++++++
 milena/sandbox/scribo/table/erase.hh               |   97 ++++++++++
 .../scribo/table/extract_lines_with_rank.hh        |  127 +++++++++++++
 .../sandbox/scribo/table/internal/align_lines.hh   |  183 ++++++++++++++++++
 .../sandbox/scribo/table/internal/connect_lines.hh |  118 ++++++++++++
 .../sandbox/scribo/table/internal/repair_lines.hh  |  132 +++++++++++++
 .../scribo/table/repair_horizontal_lines.hh        |   92 +++++++++
 .../sandbox/scribo/table/repair_vertical_lines.hh  |   98 ++++++++++
 milena/sandbox/scribo/text/extract_bboxes.hh       |   95 ++++++++++
 .../scribo/text/grouping/group_from_double_link.hh |  108 +++++++++++
 .../text/grouping/group_from_multiple_links.hh     |  154 +++++++++++++++
 .../scribo/text/grouping/group_from_single_link.hh |  101 ++++++++++
 .../text/grouping/group_with_multiple_links.hh     |  115 ++++++++++++
 .../text/grouping/group_with_single_left_link.hh   |  135 +++++++++++++
 .../text/grouping/group_with_single_right_link.hh  |  135 +++++++++++++
 .../grouping/internal/find_root.hh}                |   82 +++++----
 .../grouping/internal/init_link_array.hh}          |   79 +++++----
 .../text/grouping/internal/update_link_array.hh    |   93 +++++++++
 .../text/grouping/internal/update_link_graph.hh    |   92 +++++++++
 milena/sandbox/scribo/text/recognition.hh          |  142 ++++++++++++++
 42 files changed, 3970 insertions(+), 200 deletions(-)
 create mode 100644 milena/sandbox/scribo/core/central_sites.hh
 create mode 100644 milena/sandbox/scribo/core/component_bboxes.hh
 copy milena/sandbox/scribo/{photo.cc => core/erase_bboxes.hh} (51%)
 copy milena/sandbox/scribo/{photo.cc => core/macros.hh} (52%)
 copy milena/sandbox/scribo/{photo.cc => debug/save_label_image.hh} (52%)
 create mode 100644 milena/sandbox/scribo/debug/save_linked_textbboxes_image.hh
 create mode 100644 milena/sandbox/scribo/debug/save_table_image.hh
 create mode 100644 milena/sandbox/scribo/debug/save_textbboxes_image.hh
 create mode 100644 milena/sandbox/scribo/draw/bounding_box_links.hh
 create mode 100644 milena/sandbox/scribo/draw/bounding_boxes.hh
 copy milena/sandbox/scribo/{photo.cc => make/debug_filename.hh} (52%)
 create mode 100644 milena/sandbox/scribo/make/influence_zone_graph.hh
 create mode 100644 milena/sandbox/scribo/src/extract_text_double_link.cc
 create mode 100644 milena/sandbox/scribo/src/extract_text_multiple_links.cc
 create mode 100644 milena/sandbox/scribo/src/extract_text_single_link.cc
 copy milena/sandbox/scribo/{ => src}/photo.cc (100%)
 rename milena/sandbox/scribo/{ => src}/table.cc (100%)
 create mode 100644 milena/sandbox/scribo/table/align_lines_horizontaly.hh
 create mode 100644 milena/sandbox/scribo/table/align_lines_verticaly.hh
 create mode 100644 milena/sandbox/scribo/table/connect_horizontal_lines.hh
 create mode 100644 milena/sandbox/scribo/table/connect_vertical_lines.hh
 create mode 100644 milena/sandbox/scribo/table/erase.hh
 create mode 100644 milena/sandbox/scribo/table/extract_lines_with_rank.hh
 create mode 100644 milena/sandbox/scribo/table/internal/align_lines.hh
 create mode 100644 milena/sandbox/scribo/table/internal/connect_lines.hh
 create mode 100644 milena/sandbox/scribo/table/internal/repair_lines.hh
 create mode 100644 milena/sandbox/scribo/table/repair_horizontal_lines.hh
 create mode 100644 milena/sandbox/scribo/table/repair_vertical_lines.hh
 create mode 100644 milena/sandbox/scribo/text/extract_bboxes.hh
 create mode 100644 milena/sandbox/scribo/text/grouping/group_from_double_link.hh
 create mode 100644 milena/sandbox/scribo/text/grouping/group_from_multiple_links.hh
 create mode 100644 milena/sandbox/scribo/text/grouping/group_from_single_link.hh
 create mode 100644 milena/sandbox/scribo/text/grouping/group_with_multiple_links.hh
 create mode 100644 milena/sandbox/scribo/text/grouping/group_with_single_left_link.hh
 create mode 100644 milena/sandbox/scribo/text/grouping/group_with_single_right_link.hh
 copy milena/sandbox/scribo/{photo.cc => text/grouping/internal/find_root.hh} (52%)
 rename milena/sandbox/scribo/{photo.cc => text/grouping/internal/init_link_array.hh} (52%)
 create mode 100644 milena/sandbox/scribo/text/grouping/internal/update_link_array.hh
 create mode 100644 milena/sandbox/scribo/text/grouping/internal/update_link_graph.hh
 create mode 100644 milena/sandbox/scribo/text/recognition.hh
diff --git a/milena/sandbox/ChangeLog b/milena/sandbox/ChangeLog
index 4b4d75e..beea9cf 100644
--- a/milena/sandbox/ChangeLog
+++ b/milena/sandbox/ChangeLog
@@ -1,3 +1,55 @@
+2009-03-10  Guillaume Lazzara  <z(a)lrde.epita.fr>
+
+	Split Scribo's code into several routines.
+
+	* scribo/Makefile: update include path.
+
+	* scribo/core/central_sites.hh,
+	* scribo/core/component_bboxes.hh,
+	* scribo/core/erase_bboxes.hh,
+	* scribo/core/macros.hh,
+	* scribo/debug/save_label_image.hh,
+	* scribo/debug/save_linked_textbboxes_image.hh,
+	* scribo/debug/save_table_image.hh,
+	* scribo/debug/save_textbboxes_image.hh,
+	* scribo/draw/bounding_box_links.hh,
+	* scribo/draw/bounding_boxes.hh,
+	* scribo/make/debug_filename.hh,
+	* scribo/make/influence_zone_graph.hh,
+	* scribo/table/align_lines_horizontaly.hh,
+	* scribo/table/align_lines_verticaly.hh,
+	* scribo/table/connect_horizontal_lines.hh,
+	* scribo/table/connect_vertical_lines.hh,
+	* scribo/table/erase.hh,
+	* scribo/table/extract_lines_with_rank.hh,
+	* scribo/table/internal/align_lines.hh,
+	* scribo/table/internal/connect_lines.hh,
+	* scribo/table/internal/repair_lines.hh,
+	* scribo/table/repair_horizontal_lines.hh,
+	* scribo/table/repair_vertical_lines.hh,
+	* scribo/text/extract_bboxes.hh,
+	* scribo/text/grouping/group_from_double_link.hh,
+	* scribo/text/grouping/group_from_multiple_links.hh,
+	* scribo/text/grouping/group_from_single_link.hh,
+	* scribo/text/grouping/group_with_multiple_links.hh,
+	* scribo/text/grouping/group_with_single_left_link.hh,
+	* scribo/text/grouping/group_with_single_right_link.hh,
+	* scribo/text/grouping/internal/find_root.hh,
+	* scribo/text/grouping/internal/init_link_array.hh,
+	* scribo/text/grouping/internal/update_link_array.hh,
+	* scribo/text/grouping/internal/update_link_graph.hh,
+	* scribo/text/recognition.hh: new routines.
+
+	* scribo/photo.cc,
+	* scribo/table.cc: move...
+	* scribo/src/photo.cc,
+	* scribo/src/table.cc: ... here.
+
+	* scribo/src/extract_text_double_link.cc,
+	* scribo/src/extract_text_multiple_links.cc,
+	* scribo/src/extract_text_single_link.cc: new small test related to
+	text extraction.
+
 2009-03-10  Fabien Freling  <fabien.freling(a)lrde.epita.fr>
 
 	Implement 3D watershed and various tools.
diff --git a/milena/sandbox/scribo/Makefile b/milena/sandbox/scribo/Makefile
index 5d09e02..cbb462b 100644
--- a/milena/sandbox/scribo/Makefile
+++ b/milena/sandbox/scribo/Makefile
@@ -1,16 +1,31 @@
 all: table photo
 
 tabledbg: demat.hh
-	g++ -Wextra -Wall -I../.. -I$(HOME)/local/include  -O1 -g table.cc $(HOME)/local/lib/libtesseract_full.a -lpthread -o table
+	g++ -Wextra -Wall -I../.. -I../ -I$(HOME)/local/include -O1 -g src/table.cc $(HOME)/local/lib/libtesseract_full.a -lpthread -o bin/table.out
 
 table: demat.hh
-	g++ -Wextra -Wall -I../.. -I$(HOME)/local/include  -O1 -DNDEBUG table.cc $(HOME)/local/lib/libtesseract_full.a -lpthread -o table
+	g++ -Wextra -Wall -I../.. -I. -I.. -I$(HOME)/local/include -O1 -DNDEBUG src/table.cc $(HOME)/local/lib/libtesseract_full.a -lpthread -o bin/table.out
+
+oldtable: demat.old.hh
+	g++ -Wextra -Wall -I../.. -I../ -I$(HOME)/local/include -O1 -DNDEBUG src/table_old.cc $(HOME)/local/lib/libtesseract_full.a -lpthread -o bin/table_old.out
 
 photo: demat.hh
-	g++ -Wextra -Wall -I../.. -I$(HOME)/local/include  -O1 -DNDEBUG photo.cc $(HOME)/local/lib/libtesseract_full.a -lpthread -o photo
+	g++ -Wextra -Wall -I../.. -I. -I.. -I$(HOME)/local/include -O1 -DNDEBUG src/photo.cc $(HOME)/local/lib/libtesseract_full.a -lpthread -o bin/photo.out
+
+
+
+extract_text_single_link:
+	g++ -Wextra -Wall -I../.. -I../ -I$(HOME)/local/include -O1 -DNDEBUG src/extract_text_single_link.cc -o bin/extract_text_single_link.out
+
+extract_text_double_link:
+	g++ -Wextra -Wall -I../.. -I../ -I$(HOME)/local/include -O1 -DNDEBUG src/extract_text_double_link.cc -o bin/extract_text_double_link.out
+
+extract_text_multiple_links:
+	g++ -Wextra -Wall -I../.. -I../ -I$(HOME)/local/include -O1 -DNDEBUG src/extract_text_multiple_links.cc -o bin/extract_text_multiple_links.out
+
 
 clean:
 	rm *.ppm *.pgm *.pbm
 
 dist-clean: clean
-	rm -f table photo
+	rm -f bin/table.out bin/photo.out
diff --git a/milena/sandbox/scribo/core/central_sites.hh b/milena/sandbox/scribo/core/central_sites.hh
new file mode 100644
index 0000000..fe47625
--- /dev/null
+++ b/milena/sandbox/scribo/core/central_sites.hh
@@ -0,0 +1,97 @@
+// 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.
+
+#ifndef SCRIBO_CORE_CENTRAL_SITES_HH
+# define SCRIBO_CORE_CENTRAL_SITES_HH
+
+/// \file scribo/core/central_sites.hh
+///
+/// Returns the edge central sites of a box.
+
+# include <mln/util/couple.hh>
+# include <mln/core/site_set/box.hh>
+
+namespace scribo
+{
+  using namespace mln;
+
+  /// Returns the edge central sites of a box
+  ///
+  /// \param[in] b the bbbox
+  /// \param[in] dim the dimension used to compute the site.
+  /*!
+  **
+  ** If dim == 0, returns the left and right central sites.
+  **
+  **	|-------| \n
+  **	X       X \n
+  **	|-------| \n
+  **
+  ** If dim == 1, returns the top and bottom central sites.
+  **
+  **	|---X---| \n
+  **	|       | \n
+  **	|---X---| \n
+  **
+  **
+  **	... And so on.
+  **
+  */
+  template <typename P>
+  util::couple<P,P>
+  central_sites(const box<P>& b, unsigned dim);
+
+
+# ifndef MLN_INCLUDE_ONLY
+
+
+  template <typename P>
+  inline
+  util::couple<P,P>
+  central_sites(const box<P>& b, unsigned dim)
+  {
+    trace::entering("scribo::central_sites");
+    mln_precondition(b.is_valid());
+
+    unsigned n = b.pmax()[dim] - b.pmin()[dim];
+
+    P p1 = b.center();
+    p1[dim] -= n / 2;
+    P p2 = b.center();
+    p2[dim] += n / 2;
+
+    trace::exiting("scribo::central_sites");
+    return make::couple(p1, p2);
+  }
+
+# endif // ! MLN_INCLUDE_ONLY
+
+} // end of namespace scribo
+
+
+#endif // ! SCRIBO_CORE_CENTRAL_SITES_HH
diff --git a/milena/sandbox/scribo/core/component_bboxes.hh b/milena/sandbox/scribo/core/component_bboxes.hh
new file mode 100644
index 0000000..5af99fc
--- /dev/null
+++ b/milena/sandbox/scribo/core/component_bboxes.hh
@@ -0,0 +1,85 @@
+// 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.
+
+
+#ifndef SCRIBO_CORE_COMPONENT_BBOXES_HH
+# define SCRIBO_CORE_COMPONENT_BBOXES_HH
+
+/// \file scribo/core/component_bboxes.hh
+///
+/// Extract the bounding boxes of image components.
+
+# include <mln/core/concept/image.hh>
+# include <mln/core/concept/neighborhood.hh>
+# include <mln/core/site_set/box.hh>
+
+# include <mln/labeling/blobs.hh>
+# include <mln/labeling/compute.hh>
+
+# include <mln/util/array.hh>
+
+
+namespace scribo
+{
+
+  /// Extract the components bboxes.
+  template <typename I, typename N, typename V>
+  util::array< box<mln_site(I)> >
+  component_bboxes(const Image<I>& input,
+		   const Neighborhood<N>& nbh, const V& label_type);
+
+# ifndef MLN_INCLUDE_ONLY
+
+  template <typename I, typename N, typename V>
+  inline
+  util::array< box<mln_site(I)> >
+  component_bboxes(const Image<I>& input,
+		   const Neighborhood<N>& nbh, const V&)
+  {
+    trace::entering("scribo::component_bboxes");
+
+    mlc_equal(mln_value(I),bool)::check();
+    mlc_is_a(V, mln::value::Symbolic)::check();
+    mln_precondition(exact(input).is_valid());
+    mln_precondition(exact(nbh).is_valid());
+
+    V nbboxes;
+    mln_ch_value(I,V) lbl = labeling::blobs(input, nbh, nbboxes);
+    typedef util::array< box<mln_site(I)> > bboxes_t;
+    bboxes_t bboxes = labeling::compute(accu::meta::bbox(), lbl, nbboxes);
+
+    trace::exiting("scribo::component_bboxes");
+    return bboxes;
+  }
+
+# endif // ! MLN_INCLUDE_ONLY
+
+} // end of namespace scribo
+
+
+#endif // ! SCRIBO_CORE_COMPONENT_BBOXES_HH
diff --git a/milena/sandbox/scribo/photo.cc b/milena/sandbox/scribo/core/erase_bboxes.hh
similarity index 51%
copy from milena/sandbox/scribo/photo.cc
copy to milena/sandbox/scribo/core/erase_bboxes.hh
index a173750..cea998b 100644
--- a/milena/sandbox/scribo/photo.cc
+++ b/milena/sandbox/scribo/core/erase_bboxes.hh
@@ -1,4 +1,5 @@
-// Copyright (C) 2008 EPITA Research and Development Laboratory
+// 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
@@ -25,40 +26,56 @@
 // reasons why the executable file might be covered by the GNU General
 // Public License.
 
+#ifndef SCRIBO_CORE_ERASE_BBOXES_HH
+# define SCRIBO_CORE_ERASE_BBOXES_HH
 
-#include "demat.hh"
+/// \file scribo/core/erase_bboxes.hh
+///
+/// Remove the content of bounding boxes from an image.
 
-int main(int argc, char*argv[])
+# include <mln/core/concept/image.hh>
+# include <mln/core/site_set/box.hh>
+# include <mln/data/paste.hh>
+# include <mln/pw/all.hh>
+# include <mln/util/array.hh>
+
+# include <scribo/core/macros.hh>
+
+namespace scribo
 {
-  using namespace mln;
-  using value::int_u8;
 
-  if (argc < 2)
+  /// Remove the content of bounding boxes from an image.
+  template <typename I>
+  void
+  erase_bboxes(Image<I>& input_,
+	       const util::array< box<mln_site(I)> >& bboxes);
+
+
+# ifndef MLN_INCLUDE_ONLY
+
+
+  template <typename I>
+  void
+  erase_bboxes(Image<I>& input_,
+	       const util::array< box<mln_site(I)> >& bboxes)
   {
-    std::cout << argv[0] << " <in.pbm> <out.pgm> <l> <bbox_larger> <bbox_distance> <min_comp_nsites>" << std::endl
-	      << std::endl << std::endl
-	      << std::endl
-	      << "=========="
-	      << std::endl << std::endl
-	      << "<in.pbm>	    B/W inverted input image."
-	      << std::endl << std::endl
-/*	      << "<out.ppm>	    RGB8 output image."
-	      << std::endl << std::endl
-	      << "<bbox_distance>     Maximum distance between character bounding boxes. Used for bbox grouping."
-	      << std::endl << std::endl
-	      << "<min_comp_nsites>   Minimum site count of a character/text component."
-	      << std::endl
-	      << "		    If a component have a site count lesser than this value, it is erased."
-	      << std::endl << std::endl
-	      << std::endl*/
-	      << "=========="
-	      << std::endl << std::endl
-	      << "HINT: compile with -DNOUT to avoid debug images."
-	      << std::endl << std::endl;
-    return 1;
+    trace::entering("scribo::erase_bboxes");
+
+    mlc_equal(mln_value(I),bool)::check();
+
+    I& input = exact(input_);
+    mln_precondition(input.is_valid());
+
+    for_all_components(i, bboxes)
+      data::paste((pw::cst(false) | bboxes[i] |
+	    (pw::value(input) == true)), input);
+
+    trace::exiting("scribo::erase_bboxes");
   }
 
-  scribo::demat_photo(argv);
 
-  return 0;
-}
+# endif // ! MLN_INCLUDE_ONLY
+
+} // end of namespace scribo
+
+#endif // ! SCRIBO_CORE_ERASE_BBOXES_HH
diff --git a/milena/sandbox/scribo/photo.cc b/milena/sandbox/scribo/core/macros.hh
similarity index 52%
copy from milena/sandbox/scribo/photo.cc
copy to milena/sandbox/scribo/core/macros.hh
index a173750..91a335c 100644
--- a/milena/sandbox/scribo/photo.cc
+++ b/milena/sandbox/scribo/core/macros.hh
@@ -1,4 +1,5 @@
-// Copyright (C) 2008 EPITA Research and Development Laboratory
+// 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
@@ -25,40 +26,16 @@
 // reasons why the executable file might be covered by the GNU General
 // Public License.
 
+#ifndef SCRIBO_CORE_MACROS_HH
+# define SCRIBO_CORE_MACROS_HH
 
-#include "demat.hh"
+# define for_all_ncomponents(C, NCOMP) \
+  for (unsigned C = 1; C <= NCOMP; ++C)
 
-int main(int argc, char*argv[])
-{
-  using namespace mln;
-  using value::int_u8;
+# define for_all_components(C, S) \
+  for (unsigned C = 1; C < S.nelements(); ++C)
 
-  if (argc < 2)
-  {
-    std::cout << argv[0] << " <in.pbm> <out.pgm> <l> <bbox_larger> <bbox_distance> <min_comp_nsites>" << std::endl
-	      << std::endl << std::endl
-	      << std::endl
-	      << "=========="
-	      << std::endl << std::endl
-	      << "<in.pbm>	    B/W inverted input image."
-	      << std::endl << std::endl
-/*	      << "<out.ppm>	    RGB8 output image."
-	      << std::endl << std::endl
-	      << "<bbox_distance>     Maximum distance between character bounding boxes. Used for bbox grouping."
-	      << std::endl << std::endl
-	      << "<min_comp_nsites>   Minimum site count of a character/text component."
-	      << std::endl
-	      << "		    If a component have a site count lesser than this value, it is erased."
-	      << std::endl << std::endl
-	      << std::endl*/
-	      << "=========="
-	      << std::endl << std::endl
-	      << "HINT: compile with -DNOUT to avoid debug images."
-	      << std::endl << std::endl;
-    return 1;
-  }
+# define for_all_elements(E, S) \
+  for (unsigned E = 0; E < S.nelements(); ++E)
 
-  scribo::demat_photo(argv);
-
-  return 0;
-}
+#endif // ! SCRIBO_CORE_MACROS_HH
diff --git a/milena/sandbox/scribo/photo.cc b/milena/sandbox/scribo/debug/save_label_image.hh
similarity index 52%
copy from milena/sandbox/scribo/photo.cc
copy to milena/sandbox/scribo/debug/save_label_image.hh
index a173750..e9b01f1 100644
--- a/milena/sandbox/scribo/photo.cc
+++ b/milena/sandbox/scribo/debug/save_label_image.hh
@@ -1,4 +1,5 @@
-// Copyright (C) 2008 EPITA Research and Development Laboratory
+// 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
@@ -26,39 +27,49 @@
 // Public License.
 
 
-#include "demat.hh"
+#ifndef SCRIBO_MAKE_DEBUG_SAVE_LABEL_IMAGE_HH
+# define SCRIBO_MAKE_DEBUG_SAVE_LABEL_IMAGE_HH
 
-int main(int argc, char*argv[])
+/// \file scribo/debug/save_label_image.hh
+///
+/// Save a labeled image in a color image.
+
+namespace scribo
 {
-  using namespace mln;
-  using value::int_u8;
 
-  if (argc < 2)
+  namespace debug
   {
-    std::cout << argv[0] << " <in.pbm> <out.pgm> <l> <bbox_larger> <bbox_distance> <min_comp_nsites>" << std::endl
-	      << std::endl << std::endl
-	      << std::endl
-	      << "=========="
-	      << std::endl << std::endl
-	      << "<in.pbm>	    B/W inverted input image."
-	      << std::endl << std::endl
-/*	      << "<out.ppm>	    RGB8 output image."
-	      << std::endl << std::endl
-	      << "<bbox_distance>     Maximum distance between character bounding boxes. Used for bbox grouping."
-	      << std::endl << std::endl
-	      << "<min_comp_nsites>   Minimum site count of a character/text component."
-	      << std::endl
-	      << "		    If a component have a site count lesser than this value, it is erased."
-	      << std::endl << std::endl
-	      << std::endl*/
-	      << "=========="
-	      << std::endl << std::endl
-	      << "HINT: compile with -DNOUT to avoid debug images."
-	      << std::endl << std::endl;
-    return 1;
-  }
-
-  scribo::demat_photo(argv);
-
-  return 0;
-}
+
+    /// Save a labeled image in a color image.
+    template <typename I>
+    void
+    save_label_image(const Image<I>& lbl, const mln_value(I)& nlabels,
+		     const char *filename);
+
+
+# ifndef MLN_INCLUDE_ONLY
+
+    template <typename I>
+    inline
+    void
+    save_label_image(const Image<I>& lbl, const mln_value(I)& nlabels,
+		     const char *filename)
+    {
+      trace::entering("scribo::debug::save_label_image");
+      mlc_is_a(mln_value(I), mln::value::Symbolic)::check();
+      mln_precondition(exact(lbl).is_valid());
+
+      io::ppm::save(debug::colorize(rgb8(), lbl, nlabels), filename);
+
+      trace::exiting("scribo::debug::save_label_image");
+    }
+
+
+# endif // ! MLN_INCLUDE_ONLY
+
+  } // end of namespace scribo::debug
+
+} // end of namespace scribo
+
+
+#endif // ! SCRIBO_MAKE_DEBUG_SAVE_LABEL_IMAGE_HH
diff --git a/milena/sandbox/scribo/debug/save_linked_textbboxes_image.hh b/milena/sandbox/scribo/debug/save_linked_textbboxes_image.hh
new file mode 100644
index 0000000..a7f8927
--- /dev/null
+++ b/milena/sandbox/scribo/debug/save_linked_textbboxes_image.hh
@@ -0,0 +1,170 @@
+// Copyright (C) 2009 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, 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.
+
+
+#ifndef SCRIBO_DEBUG_SAVE_LINKED_TEXTBBOXES_IMAGE_HH
+# define SCRIBO_DEBUG_SAVE_LINKED_TEXTBBOXES_IMAGE_HH
+
+/// \file scribo/draw/bounding_boxes.hh
+///
+/// Save the bounding box links image.
+
+# include <mln/core/concept/image.hh>
+# include <mln/core/concept/graph.hh>
+# include <mln/level/convert.hh>
+# include <mln/value/rgb8.hh>
+# include <mln/util/array.hh>
+# include <mln/io/ppm/save.hh>
+
+# include <scribo/draw/bounding_boxes.hh>
+# include <scribo/draw/bounding_box_links.hh>
+
+
+namespace scribo
+{
+
+  namespace debug
+  {
+
+    using namespace mln;
+
+
+    /// Save the bounding box links image.
+    template <typename I>
+    void
+    save_linked_textbboxes_image(const Image<I>& input,
+				 const util::array< box<mln_site(I)> >& textbboxes,
+				 const util::array<unsigned>& link_array,
+				 const value::rgb8& box_value,
+				 const value::rgb8& link_value,
+				 const std::string& filename);
+
+
+    template <typename I>
+    void
+    save_linked_textbboxes_image(const Image<I>& input,
+				 const util::array< box<mln_site(I)> >& textbboxes,
+				 const util::array<unsigned>& left_link,
+				 const util::array<unsigned>& right_array,
+				 const value::rgb8& box_value,
+				 const value::rgb8& left_link_value,
+				 const value::rgb8& right_link_value,
+				 const std::string& filename);
+
+
+    template <typename I, typename G>
+    void
+    save_linked_textbboxes_image(const Image<I>& input,
+				 const util::array< box<mln_site(I)> >& textbboxes,
+				 const Graph<G>& g,
+				 const value::rgb8& box_value,
+				 const value::rgb8& link_value,
+				 const std::string& filename);
+
+
+# ifndef MLN_INCLUDE_ONLY
+
+
+    template <typename I>
+    inline
+    void
+    save_linked_textbboxes_image(const Image<I>& input,
+				 const util::array< box<mln_site(I)> >& textbboxes,
+				 const util::array<unsigned>& link_array,
+				 const value::rgb8& box_value,
+				 const value::rgb8& link_value,
+				 const std::string& filename)
+    {
+      trace::entering("scribo::debug::save_linked_textbboxes_image");
+      mln_precondition(exact(input).is_valid());
+
+      mln_ch_value(I,value::rgb8) tmp = level::convert(value::rgb8(), input);
+      draw::bounding_boxes(tmp, textbboxes, box_value);
+      draw::bounding_box_links(tmp, textbboxes, link_array, link_value);
+      io::ppm::save(tmp, filename);
+
+      trace::exiting("scribo::debug::save_linked_textbboxes_image");
+    }
+
+
+    template <typename I>
+    inline
+    void
+    save_linked_textbboxes_image(const Image<I>& input,
+				 const util::array< box<mln_site(I)> >& textbboxes,
+				 const util::array<unsigned>& left_link,
+				 const util::array<unsigned>& right_link,
+				 const value::rgb8& box_value,
+				 const value::rgb8& left_value,
+				 const value::rgb8& right_value,
+				 const std::string& filename)
+    {
+      trace::entering("scribo::debug::save_linked_textbboxes_image");
+      mln_precondition(exact(input).is_valid());
+
+      mln_ch_value(I,value::rgb8) tmp = level::convert(value::rgb8(), input);
+      draw::bounding_boxes(tmp, textbboxes, box_value);
+      draw::bounding_box_links(tmp, textbboxes,
+			       left_link, right_link,
+			       left_value, right_value);
+      io::ppm::save(tmp, filename);
+
+      trace::exiting("scribo::debug::save_linked_textbboxes_image");
+    }
+
+
+    template <typename I, typename G>
+    inline
+    void
+    save_linked_textbboxes_image(const Image<I>& input,
+				 const util::array< box<mln_site(I)> >& textbboxes,
+				 const Graph<G>& g,
+				 const value::rgb8& box_value,
+				 const value::rgb8& link_value,
+				 const std::string& filename)
+    {
+      trace::entering("scribo::debug::save_linked_textbboxes_image");
+      mln_precondition(exact(g).is_valid());
+      mln_precondition(exact(input).is_valid());
+
+      mln_ch_value(I,value::rgb8) tmp = level::convert(value::rgb8(), input);
+      draw::bounding_boxes(tmp, textbboxes, box_value);
+      draw::bounding_box_links(tmp, textbboxes, g, link_value);
+      io::ppm::save(tmp, filename);
+
+      trace::exiting("scribo::debug::save_linked_textbboxes_image");
+    }
+
+
+# endif // ! MLN_INCLUDE_ONLY
+
+  } // end of namespace scribo::debug
+
+} // end of namespace scribo
+
+
+#endif // ! SCRIBO_DEBUG_SAVE_LINKED_TEXTBBOXES_IMAGE_HH
diff --git a/milena/sandbox/scribo/debug/save_table_image.hh b/milena/sandbox/scribo/debug/save_table_image.hh
new file mode 100644
index 0000000..b170420
--- /dev/null
+++ b/milena/sandbox/scribo/debug/save_table_image.hh
@@ -0,0 +1,97 @@
+// 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.
+
+#ifndef SCRIBO_DEBUG_SAVE_TABLE_IMAGE_HH
+# define SCRIBO_DEBUG_SAVE_TABLE_IMAGE_HH
+
+/// \file scribo/debug/save_table_image.hh
+///
+/// Save table line bounding boxes in an image.
+
+# include <string>
+
+# include <mln/core/concept/image.hh>
+# include <mln/data/fill.hh>
+# include <mln/util/array.hh>
+# include <mln/util/couple.hh>
+# include <mln/value/rgb8.hh>
+# include <mln/io/ppm/save.hh>
+
+# include <scribo/draw/bounding_boxes.hh>
+
+namespace scribo
+{
+
+  namespace debug
+  {
+
+    /// Save lines bounding boxes in an image filled with \p bg_color.
+    /// Bounding boxes are displayed with \p bbox_color.
+    template <typename I>
+    void
+    save_table(const Image<I>& input_,
+	       util::couple<util::array<box<mln_site(I)> >,
+			    util::array<box<mln_site(I)> > > tableboxes,
+	       const value::rgb8& bg_color,
+	       const value::rgb8& bbox_color,
+	       const std::string& filename);
+
+
+# ifndef MLN_INCLUDE_ONLY
+
+
+    template <typename I>
+    void
+    save_table(const Image<I>& input_,
+	       util::couple<util::array<box<mln_site(I)> >,
+			    util::array<box<mln_site(I)> > > tableboxes,
+	       const value::rgb8& bg_color,
+	       const value::rgb8& bbox_color,
+	       const std::string& filename)
+    {
+      trace::entering("scribo::debug::save_table_image");
+      const I& input = exact(input_);
+      mln_precondition(input.is_valid());
+
+      mln_ch_value(I,value::rgb8) out2(exact(input).domain());
+      data::fill(out2, bg_color);
+      draw::bounding_boxes(out2, tableboxes.first(), bbox_color);
+      draw::bounding_boxes(out2, tableboxes.second(), bbox_color);
+      io::ppm::save(out2, filename);
+
+      trace::exiting("scribo::internal::save_table");
+    }
+
+# endif // ! MLN_INCLUDE_ONLY
+
+  } // end of namespace scribo::debug
+
+} // end of namespace scribo
+
+
+#endif // ! SCRIBO_DEBUG_SAVE_TABLE_IMAGE_HH
diff --git a/milena/sandbox/scribo/debug/save_textbboxes_image.hh b/milena/sandbox/scribo/debug/save_textbboxes_image.hh
new file mode 100644
index 0000000..b210b39
--- /dev/null
+++ b/milena/sandbox/scribo/debug/save_textbboxes_image.hh
@@ -0,0 +1,90 @@
+// Copyright (C) 2009 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, 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.
+
+
+#ifndef SCRIBO_DEBUG_SAVE_TEXTBBOXES_IMAGE_HH
+# define SCRIBO_DEBUG_SAVE_TEXTBBOXES_IMAGE_HH
+
+/// \file scribo/draw/bounding_boxes.hh
+///
+/// Draw a list of bounding boxes and their associated mass center.
+
+# include <mln/core/concept/image.hh>
+# include <mln/level/convert.hh>
+# include <mln/value/rgb8.hh>
+# include <mln/util/array.hh>
+# include <mln/io/ppm/save.hh>
+
+# include <scribo/draw/bounding_boxes.hh>
+
+
+namespace scribo
+{
+
+  namespace debug
+  {
+
+    using namespace mln;
+
+
+    /// Draw a list of bounding boxes and their associated mass center.
+    template <typename I>
+    void
+    save_textbboxes_image(const Image<I>& input,
+			  const util::array< box<mln_site(I)> >& textbboxes,
+			  const value::rgb8& value,
+			  const std::string& filename);
+
+
+# ifndef MLN_INCLUDE_ONLY
+
+    template <typename I>
+    inline
+    void
+    save_textbboxes_image(const Image<I>& input,
+			  const util::array< box<mln_site(I)> >& textbboxes,
+			  const value::rgb8& value,
+			  const std::string& filename)
+    {
+      trace::entering("scribo::debug::save_textbboxes_image");
+      mln_precondition(exact(input).is_valid());
+
+      mln_ch_value(I,value::rgb8) tmp = level::convert(value::rgb8(), input);
+      draw::bounding_boxes(tmp, textbboxes, value);
+      io::ppm::save(tmp, filename);
+
+      trace::exiting("scribo::debug::save_textbboxes_image");
+    }
+
+# endif // ! MLN_INCLUDE_ONLY
+
+  } // end of namespace scribo::debug
+
+} // end of namespace scribo
+
+
+#endif // ! SCRIBO_DEBUG_SAVE_TEXTBBOXES_IMAGE_HH
diff --git a/milena/sandbox/scribo/draw/bounding_box_links.hh b/milena/sandbox/scribo/draw/bounding_box_links.hh
new file mode 100644
index 0000000..7cf57ac
--- /dev/null
+++ b/milena/sandbox/scribo/draw/bounding_box_links.hh
@@ -0,0 +1,197 @@
+// Copyright (C) 2009 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, 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.
+
+
+#ifndef SCRIBO_DRAW_BOUNDING_BOX_LINKS_HH
+# define SCRIBO_DRAW_BOUNDING_BOX_LINKS_HH
+
+/// \file scribo/draw/bounding_boxes.hh
+///
+/// Draw a list of bounding box links.
+
+# include <mln/core/concept/image.hh>
+# include <mln/draw/line.hh>
+# include <mln/util/array.hh>
+# include <mln/canvas/browsing/depth_first_search.hh>
+
+# include <scribo/core/macros.hh>
+
+namespace scribo
+{
+
+  namespace draw
+  {
+
+    using namespace mln;
+
+    /// Draw a list of bounding box links.
+    template <typename I>
+    void
+    bounding_box_links(Image<I>& input_,
+		       const util::array< box<mln_site(I)> >& bboxes,
+		       const util::array<unsigned>& link_array,
+		       const mln_value(I)& value);
+
+
+    template <typename I>
+    void
+    bounding_box_links(Image<I>& input,
+		       const util::array< box<mln_site(I)> >& bboxes,
+		       const util::array<unsigned>& left_link,
+		       const util::array<unsigned>& right_link,
+		       const mln_value(I)& left_link_value,
+		       const mln_value(I)& right_link_value);
+
+
+# ifndef MLN_INCLUDE_ONLY
+
+
+    namespace internal
+    {
+      /// Functor to be passed to depth_first_search.
+      template <typename I>
+      struct draw_graph_edges_functor
+      {
+
+	draw_graph_edges_functor(I& ima,
+				 const util::array<box<mln_site(I)> >& textbboxes,
+				 const mln_value(I)& value)
+	  : ima_(ima), textbboxes_(textbboxes), value_(value)
+	{}
+
+	template <typename G>
+	void
+	init(const Graph<G>& g)
+	{ deja_vu.resize(exact(g).v_nmax(), false); }
+
+	void final()
+	{}
+
+	void next_component()
+	{}
+
+	void new_component_from_vertex(unsigned)
+	{}
+
+	void added_to_queue(unsigned id)
+	{
+	  deja_vu[id] = true;
+	  mln::draw::line(ima_, textbboxes_[current_vertex].center(),
+	      textbboxes_[id].center(), value_);
+	}
+
+	void process_vertex(unsigned id)
+	{ current_vertex = id; }
+
+	bool to_be_treated(unsigned id)
+	{ return ! deja_vu[id]; }
+
+	bool to_be_queued(unsigned id)
+	{ return to_be_treated(id); }
+
+	I& ima_;
+	const util::array<box<mln_site(I)> >& textbboxes_;
+	mln_value(I) value_;
+	unsigned current_vertex;
+	std::vector<bool> deja_vu;
+      };
+
+    } // end of namespace scribo::text::grouping::internal
+
+
+    template <typename I>
+    inline
+    void
+    bounding_box_links(Image<I>& input_,
+		       const util::array< box<mln_site(I)> >& bboxes,
+		       const util::array<unsigned>& link_array,
+		       const mln_value(I)& value)
+    {
+      trace::entering("scribo::draw::bounding_box_links");
+
+      I& input = exact(input_);
+
+      mln_precondition(input.is_valid());
+
+      for_all_components(i, link_array)
+	if (link_array[i] != i)
+	  mln::draw::line(input,
+			  bboxes[i].center(),
+			  bboxes[link_array[i]].center(),
+			  value);
+
+      trace::exiting("scribo::draw::bounding_box_links");
+    }
+
+
+    template <typename I>
+    inline
+    void
+    bounding_box_links(Image<I>& input,
+		       const util::array< box<mln_site(I)> >& bboxes,
+		       const util::array<unsigned>& left_link,
+		       const util::array<unsigned>& right_link,
+		       const mln_value(I)& left_link_value,
+		       const mln_value(I)& right_link_value)
+    {
+      trace::entering("scribo::draw::bounding_box_links");
+      mln_precondition(exact(input).is_valid());
+
+      bounding_box_links(input, bboxes, left_link, left_link_value);
+      bounding_box_links(input, bboxes, right_link, right_link_value);
+
+      trace::exiting("scribo::draw::bounding_box_links");
+    }
+
+
+    template <typename I, typename G>
+    inline
+    void
+    bounding_box_links(Image<I>& input,
+		       const util::array< box<mln_site(I)> >& bboxes,
+		       const Graph<G>& g,
+		       const mln_value(I)& link_value)
+    {
+      trace::entering("scribo::draw::bounding_box_links");
+      mln_precondition(exact(input).is_valid());
+      mln_precondition(exact(g).v_nmax() == bboxes.nelements());
+
+      internal::draw_graph_edges_functor<I> f(exact(input), bboxes, link_value);
+      canvas::browsing::depth_first_search(g, f);
+
+      trace::exiting("scribo::draw::bounding_box_links");
+    }
+
+
+# endif // ! MLN_INCLUDE_ONLY
+
+  } // end of namespace scribo::draw
+
+} // end of namespace scribo
+
+
+#endif // ! SCRIBO_DRAW_BOUNDING_BOX_LINKS_HH
diff --git a/milena/sandbox/scribo/draw/bounding_boxes.hh b/milena/sandbox/scribo/draw/bounding_boxes.hh
new file mode 100644
index 0000000..0db60a4
--- /dev/null
+++ b/milena/sandbox/scribo/draw/bounding_boxes.hh
@@ -0,0 +1,93 @@
+// Copyright (C) 2009 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, 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.
+
+
+#ifndef SCRIBO_DRAW_BOUNDING_BOXES_HH
+# define SCRIBO_DRAW_BOUNDING_BOXES_HH
+
+/// \file scribo/draw/bounding_boxes.hh
+///
+/// Draw a list of bounding boxes and their associated mass center.
+
+# include <mln/core/concept/image.hh>
+# include <mln/draw/box.hh>
+# include <mln/util/array.hh>
+
+# include <scribo/core/macros.hh>
+
+namespace scribo
+{
+
+  namespace draw
+  {
+
+    using namespace mln;
+
+    /// Draw a list of bounding boxes and their associated mass center.
+    template <typename I>
+    void
+    bounding_boxes(Image<I>& input_,
+		   const util::array< box<mln_site(I)> >& boxes,
+		   const mln_value(I)& value);
+
+
+
+# ifndef MLN_INCLUDE_ONLY
+
+
+    template <typename I>
+    inline
+    void
+    bounding_boxes(Image<I>& input_,
+		   const util::array< box<mln_site(I)> >& boxes,
+		   const mln_value(I)& value)
+    {
+      trace::entering("scribo::draw::bounding_boxes");
+
+      I& input = exact(input_);
+
+      mln_precondition(input.is_valid());
+
+      for_all_components(i, boxes)
+        if (boxes[i].is_valid())
+	{
+	  input(boxes[i].center()) = value;
+	  mln::draw::box(input, boxes[i], value);
+	}
+
+      trace::exiting("scribo::draw::bounding_boxes");
+    }
+
+
+# endif // ! MLN_INCLUDE_ONLY
+
+  } // end of namespace scribo::draw
+
+} // end of namespace scribo
+
+
+#endif // ! SCRIBO_DRAW_BOUNDING_BOXES_HH
diff --git a/milena/sandbox/scribo/photo.cc b/milena/sandbox/scribo/make/debug_filename.hh
similarity index 52%
copy from milena/sandbox/scribo/photo.cc
copy to milena/sandbox/scribo/make/debug_filename.hh
index a173750..b159328 100644
--- a/milena/sandbox/scribo/photo.cc
+++ b/milena/sandbox/scribo/make/debug_filename.hh
@@ -1,4 +1,5 @@
-// Copyright (C) 2008 EPITA Research and Development Laboratory
+// 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
@@ -26,39 +27,61 @@
 // Public License.
 
 
-#include "demat.hh"
+#ifndef SCRIBO_MAKE_DEBUG_FILENAME_HH
+# define SCRIBO_MAKE_DEBUG_FILENAME_HH
 
-int main(int argc, char*argv[])
+/// \file scribo/make/debug_filename.hh
+///
+/// Construct and returns a formated output file name.
+
+# include <sstream>
+
+
+namespace scribo
 {
-  using namespace mln;
-  using value::int_u8;
 
-  if (argc < 2)
+  namespace make
   {
-    std::cout << argv[0] << " <in.pbm> <out.pgm> <l> <bbox_larger> <bbox_distance> <min_comp_nsites>" << std::endl
-	      << std::endl << std::endl
-	      << std::endl
-	      << "=========="
-	      << std::endl << std::endl
-	      << "<in.pbm>	    B/W inverted input image."
-	      << std::endl << std::endl
-/*	      << "<out.ppm>	    RGB8 output image."
-	      << std::endl << std::endl
-	      << "<bbox_distance>     Maximum distance between character bounding boxes. Used for bbox grouping."
-	      << std::endl << std::endl
-	      << "<min_comp_nsites>   Minimum site count of a character/text component."
-	      << std::endl
-	      << "		    If a component have a site count lesser than this value, it is erased."
-	      << std::endl << std::endl
-	      << std::endl*/
-	      << "=========="
-	      << std::endl << std::endl
-	      << "HINT: compile with -DNOUT to avoid debug images."
-	      << std::endl << std::endl;
-    return 1;
-  }
-
-  scribo::demat_photo(argv);
-
-  return 0;
-}
+
+    /// Construct and returns a formated output file name:
+    ///
+    ///	    `input_filename`_`id`_`name`
+    std::string
+    debug_filename(const char *input_filename,
+		   const char *name);
+
+
+# ifndef MLN_INCLUDE_ONLY
+
+
+    inline
+    std::string
+    debug_filename(const char *input_filename,
+		   const char *name)
+    {
+      static int file_id = 1;
+
+      std::ostringstream os;
+      os << "./"
+	 << input_filename
+	 << "_";
+
+      if (file_id < 10)
+	os << "0";
+
+      os << file_id++
+	 << "_"
+	 << name;
+      return os.str();
+    }
+
+
+# endif // ! MLN_INCLUDE_ONLY
+
+
+  } // end of namespace scribo::make
+
+} // end of namespace scribo
+
+
+#endif // ! SCRIBO_MAKE_DEBUG_FILENAME_HH
diff --git a/milena/sandbox/scribo/make/influence_zone_graph.hh b/milena/sandbox/scribo/make/influence_zone_graph.hh
new file mode 100644
index 0000000..c3ad15e
--- /dev/null
+++ b/milena/sandbox/scribo/make/influence_zone_graph.hh
@@ -0,0 +1,121 @@
+// 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.
+
+#ifndef SCRIBO_MAKE_INFLUENCE_ZONE_GRAPH_HH
+# define SCRIBO_MAKE_INFLUENCE_ZONE_GRAPH_HH
+
+/// \file scribo/make/influence_zone_graph.hh
+///
+/// Create a region adjacency graph from the influence zone image
+/// of binary image.
+
+
+# include <mln/core/concept/image.hh>
+# include <mln/core/concept/neighborhood.hh>
+
+# include <mln/labeling/blobs.hh>
+
+# include <mln/transform/influence_zone_geodesic.hh>
+
+# include <mln/util/graph.hh>
+
+
+namespace scribo
+{
+
+  namespace text
+  {
+
+    namespace grouping
+    {
+
+      using namespace mln;
+
+
+      /// Compute a labeled image of input, then compute an influence
+      /// zone image and make a graph from it.
+      ///
+      /// \param[in] input_	a binary image.
+      /// \param[in] nbh_	a neighbordhood.
+      /// \param[in] label_type The type of this argument is used
+      ///			as label type while labeling the image.
+      /// \param[in] iz_dmax	Max distance of the influence zone.
+      ///
+      /// \return a region adjacency graph.
+      template <typename I, typename V>
+      util::graph
+      influence_zone_graph(const Image<I>& input_,
+			   const Neighborhood<N>& nbh_,
+			   const V& label_type,
+			   unsigned iz_dmax);
+
+
+# ifndef MLN_INCLUDE_ONLY
+
+      template <typename I, typename V>
+      util::graph
+      influence_zone_graph(const Image<I>& input_,
+			   const Neighborhood<N>& nbh_,
+			   const V& label_type,
+			   unsigned iz_dmax)
+      {
+	trace::entering("scribo::make::influence_zone_graph");
+
+	(void) label_type;
+	I& input = exact(input_);
+	const N& nbh = exact(nbh_);
+
+	mlc_is_equal(mln_value(I), bool)::check();
+	mln_assertion(input.is_valid());
+	mln_assertion(nbh.is_valid());
+
+	V nlabels;
+	typedef mln_ch_value(I,V) lbl_t;
+	lbl_t lbl = labeling::blobs(input, nbh, nlabels);
+
+	lbl_t iz = transform::influence_zone_geodesic(lbl, nbh, iz_dmax);
+
+	util::graph g = make::graph(iz | (pw::value(iz) != pw::cst(literal::zero)),
+				    nbh, nlabels);
+
+	trace::exiting("scribo::make::influence_zone_graph");
+	return g;
+      }
+
+# endif // ! MLN_INCLUDE_ONLY
+
+    } // end of namespace scribo::text::grouping
+
+  } // end of namespace scribo::text
+
+} // end of namespace scribo
+
+
+
+#endif // ! SCRIBO_MAKE_INFLUENCE_ZONE_GRAPH_HH
+
diff --git a/milena/sandbox/scribo/src/extract_text_double_link.cc b/milena/sandbox/scribo/src/extract_text_double_link.cc
new file mode 100644
index 0000000..26fccff
--- /dev/null
+++ b/milena/sandbox/scribo/src/extract_text_double_link.cc
@@ -0,0 +1,109 @@
+// Copyright (C) 2009 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, 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 <iostream>
+
+#include <mln/essential/2d.hh>
+
+#include <scribo/text/extract_bboxes.hh>
+#include <scribo/text/grouping/group_with_single_left_link.hh>
+#include <scribo/text/grouping/group_with_single_right_link.hh>
+#include <scribo/debug/save_linked_textbboxes_image.hh>
+#include <scribo/text/grouping/group_from_double_link.hh>
+
+#include <scribo/debug/save_textbboxes_image.hh>
+
+int usage(const char *name)
+{
+  std::cout << "Usage: " << name << " <input.pbm> " << std::endl;
+  return 1;
+}
+
+
+namespace scribo { namespace text { namespace grouping
+{
+  using namespace mln;
+
+  template <typename I>
+      util::array< box<mln_site(I)> >
+      group_from_double_link(const util::array< box<mln_site(I)> >& textbboxes,
+			     util::array<unsigned>& left_link,
+			     util::array<unsigned>& right_link);
+}}}
+
+int main(int argc, char* argv[])
+{
+  using namespace scribo;
+  using namespace mln;
+
+  if (argc < 1)
+    return usage(argv[0]);
+
+  image2d<bool> input;
+  io::pbm::load(input, argv[1]);
+
+  value::label_16 nbboxes;
+  util::array<box2d> textbboxes = text::extract_bboxes(input, c8(), nbboxes);
+
+  util::array<unsigned> left_link
+	= text::grouping::group_with_single_left_link(input,
+						      c8(), nbboxes,
+						      textbboxes, 30);
+  util::array<unsigned> right_link
+	= text::grouping::group_with_single_right_link(input,
+						       c8(), nbboxes,
+						       textbboxes, 30);
+
+  std::cout << "BEFORE - nbboxes = " << nbboxes << std::endl;
+
+  scribo::debug::save_linked_textbboxes_image(input,
+					      textbboxes, left_link,
+					      literal::red, literal::cyan,
+					      "test_double_link_left_linked.ppm");
+  scribo::debug::save_linked_textbboxes_image(input,
+					      textbboxes, right_link,
+					      literal::red, literal::cyan,
+					      "test_double_link_right_linked.ppm");
+
+  scribo::debug::save_linked_textbboxes_image(input,
+					      textbboxes, left_link, right_link,
+					      literal::red, literal::cyan,
+					      literal::yellow,
+					      "test_double_link_double_linked.ppm");
+
+  // With validation.
+  util::array< box<point2d> > grouped_textbboxes
+	= text::grouping::group_from_double_link(textbboxes, left_link, right_link);
+
+  std::cout << "AFTER double grouping - nbboxes = " << grouped_textbboxes.nelements() << std::endl;
+
+  scribo::debug::save_textbboxes_image(input, grouped_textbboxes,
+				       literal::red,
+				       "test_double_link_grouped_text.ppm");
+
+}
+
diff --git a/milena/sandbox/scribo/src/extract_text_multiple_links.cc b/milena/sandbox/scribo/src/extract_text_multiple_links.cc
new file mode 100644
index 0000000..9b181e6
--- /dev/null
+++ b/milena/sandbox/scribo/src/extract_text_multiple_links.cc
@@ -0,0 +1,78 @@
+// Copyright (C) 2009 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, 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 <iostream>
+
+#include <mln/essential/2d.hh>
+
+#include <scribo/text/extract_bboxes.hh>
+#include <scribo/text/grouping/group_with_multiple_links.hh>
+#include <scribo/text/grouping/group_from_multiple_links.hh>
+
+#include <scribo/debug/save_textbboxes_image.hh>
+#include <scribo/debug/save_linked_textbboxes_image.hh>
+
+int usage(const char *name)
+{
+  std::cout << "Usage: " << name << " <input.pbm> " << std::endl;
+  return 1;
+}
+
+int main(int argc, char* argv[])
+{
+  using namespace scribo;
+  using namespace mln;
+
+  if (argc < 1)
+    return usage(argv[0]);
+
+  image2d<bool> input;
+  io::pbm::load(input, argv[1]);
+
+  value::label_16 nbboxes;
+  util::array<box2d> textbboxes = text::extract_bboxes(input, c8(), nbboxes);
+
+  util::graph g = text::grouping::group_with_multiple_links(input,
+							    c8(), nbboxes,
+							    textbboxes, 30);
+
+  std::cout << "BEFORE - nbboxes = " << nbboxes << std::endl;
+  scribo::debug::save_linked_textbboxes_image(input,
+					      textbboxes, g,
+					      literal::red, literal::cyan,
+					      "test_multiple_links_left_linked.ppm");
+
+  util::array< box<point2d> > grouped_textbboxes
+    = text::grouping::group_from_multiple_links(textbboxes, g);
+
+  std::cout << "AFTER - nbboxes = " << grouped_textbboxes.nelements() << std::endl;
+
+  scribo::debug::save_textbboxes_image(input, grouped_textbboxes,
+				       literal::red,
+				       "test_multiple_links_grouped_text.ppm");
+}
+
diff --git a/milena/sandbox/scribo/src/extract_text_single_link.cc b/milena/sandbox/scribo/src/extract_text_single_link.cc
new file mode 100644
index 0000000..5b4e248
--- /dev/null
+++ b/milena/sandbox/scribo/src/extract_text_single_link.cc
@@ -0,0 +1,108 @@
+// Copyright (C) 2009 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, 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 <iostream>
+
+#include <mln/essential/2d.hh>
+
+#include <scribo/text/extract_bboxes.hh>
+#include <scribo/text/grouping/group_with_single_left_link.hh>
+#include <scribo/text/grouping/group_with_single_right_link.hh>
+#include <scribo/text/grouping/group_from_single_link.hh>
+
+#include <scribo/debug/save_textbboxes_image.hh>
+#include <scribo/debug/save_linked_textbboxes_image.hh>
+
+int usage(const char *name)
+{
+  std::cout << "Usage: " << name << " <input.pbm> " << std::endl;
+  return 1;
+}
+
+int main(int argc, char* argv[])
+{
+  using namespace scribo;
+  using namespace mln;
+
+  if (argc < 1)
+    return usage(argv[0]);
+
+  image2d<bool> input;
+  io::pbm::load(input, argv[1]);
+
+  value::label_16 nbboxes;
+  util::array<box2d> textbboxes = text::extract_bboxes(input, c8(), nbboxes);
+
+  {
+    std::cout << "* Left grouping" << std::endl;
+    util::array<unsigned> left_link
+	= text::grouping::group_with_single_left_link(input,
+						      c8(), nbboxes,
+						      textbboxes, 30);
+
+    std::cout << "BEFORE - nbboxes = " << nbboxes << std::endl;
+    scribo::debug::save_linked_textbboxes_image(input,
+						textbboxes, left_link,
+						literal::red, literal::cyan,
+						"test_single_left_link_linked.ppm");
+
+    util::array< box<point2d> > grouped_textbboxes
+	  = text::grouping::group_from_single_link(textbboxes, left_link);
+
+    std::cout << "AFTER - nbboxes = " << grouped_textbboxes.nelements() << std::endl;
+
+    scribo::debug::save_textbboxes_image(input, grouped_textbboxes,
+					 literal::red,
+					 "test_single_left_link_grouped_text.ppm");
+  }
+
+  {
+    std::cout << "* Left grouping" << std::endl;
+    util::array<unsigned> right_link
+	= text::grouping::group_with_single_right_link(input,
+						      c8(), nbboxes,
+						      textbboxes, 30);
+
+    std::cout << "BEFORE - nbboxes = " << nbboxes << std::endl;
+    scribo::debug::save_linked_textbboxes_image(input,
+						textbboxes, right_link,
+						literal::red, literal::cyan,
+						"test_single_right_link_linked.ppm");
+
+    util::array< box<point2d> > grouped_textbboxes
+	  = text::grouping::group_from_single_link(textbboxes, right_link);
+
+    std::cout << "AFTER - nbboxes = " << grouped_textbboxes.nelements() << std::endl;
+
+    scribo::debug::save_textbboxes_image(input, grouped_textbboxes,
+					 literal::red,
+					 "test_single_right_link_grouped_text.ppm");
+  }
+
+
+}
+
diff --git a/milena/sandbox/scribo/photo.cc b/milena/sandbox/scribo/src/photo.cc
similarity index 100%
copy from milena/sandbox/scribo/photo.cc
copy to milena/sandbox/scribo/src/photo.cc
diff --git a/milena/sandbox/scribo/table.cc b/milena/sandbox/scribo/src/table.cc
similarity index 100%
rename from milena/sandbox/scribo/table.cc
rename to milena/sandbox/scribo/src/table.cc
diff --git a/milena/sandbox/scribo/table/align_lines_horizontaly.hh b/milena/sandbox/scribo/table/align_lines_horizontaly.hh
new file mode 100644
index 0000000..e75f2f2
--- /dev/null
+++ b/milena/sandbox/scribo/table/align_lines_horizontaly.hh
@@ -0,0 +1,97 @@
+// 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.
+
+
+#ifndef SCRIBO_TABLE_ALIGN_LINES_HORIZONTALY_HH
+# define SCRIBO_TABLE_ALIGN_LINES_HORIZONTALY_HH
+
+/// \file scribo/table/align_lines_horizontaly.hh
+///
+/// Align line bounding boxes horizontaly.
+
+
+# include <mln/core/concept/image.hh>
+# include <mln/core/site_set/box.hh>
+# include <mln/geom/nrows.hh>
+# include <mln/geom/min_nrow.hh>
+# include <mln/geom/max_nrow.hh>
+
+# include <mln/util/array.hh>
+
+# include <scribo/table/internal/align_lines.hh>
+
+
+namespace scribo
+{
+
+  namespace table
+  {
+
+    /// Align line bounding boxes horizontaly.
+    ///
+    /// \param[in]	input	     Image from which the line bboxes are
+    ///				     extracted from.
+    /// \param[in, out] lines_bboxes horizontal lines bounding boxes.
+    ///
+    /// \return A list of the resulting aligned rows. Each integer is actually
+    ///		a row number.
+    template <typename I>
+    util::array<int>
+    align_lines_horizontaly(const Image<I>& input,
+			  util::array<box<mln_site(I)> >& lines_bboxes);
+
+
+# ifndef MLN_INCLUDE_ONLY
+
+
+    template <typename I>
+    util::array<int>
+    align_lines_horizontaly(const Image<I>& input,
+			    util::array<box<mln_site(I)> >& lines_bboxes)
+    {
+      trace::entering("scribo::table::align_lines_horizontaly");
+
+      mln_precondition(exact(input).is_valid());
+      util::array<int> res =  internal::align_lines(geom::nrows(input),
+						    geom::min_row(input),
+						    geom::max_row(input),
+						    lines_bboxes, 0);
+
+      trace::exiting("scribo::table::align_lines_horizontaly");
+      return res;
+    }
+
+
+# endif // ! MLN_INCLUDE_ONLY
+
+
+  } // end of namespace scribo::table
+
+} // end of namespace scribo
+
+#endif // ! SCRIBO_TABLE_ALIGN_LINES_HORIZONTALY_HH
diff --git a/milena/sandbox/scribo/table/align_lines_verticaly.hh b/milena/sandbox/scribo/table/align_lines_verticaly.hh
new file mode 100644
index 0000000..7e860d0
--- /dev/null
+++ b/milena/sandbox/scribo/table/align_lines_verticaly.hh
@@ -0,0 +1,98 @@
+// 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.
+
+
+#ifndef SCRIBO_TABLE_ALIGN_LINES_VERTICALY_HH
+# define SCRIBO_TABLE_ALIGN_LINES_VERTICALY_HH
+
+/// \file scribo/table/align_lines_verticaly.hh
+///
+/// Align line bounding boxes verticaly.
+
+
+# include <mln/core/concept/image.hh>
+# include <mln/core/site_set/box.hh>
+# include <mln/geom/ncols.hh>
+# include <mln/geom/min_ncol.hh>
+# include <mln/geom/max_ncol.hh>
+
+# include <mln/util/array.hh>
+
+# include <scribo/table/internal/align_lines.hh>
+
+
+
+namespace scribo
+{
+
+  namespace table
+  {
+
+    /// Align line bounding boxes verticaly.
+    ///
+    /// \param[in]	input	     Image from which the line bboxes are
+    ///				     extracted from.
+    /// \param[in, out] lines_bboxes vertical lines bounding boxes.
+    ///
+    /// \return A list of the resulting aligned cols. Each integer is actually
+    ///		a col number.
+    template <typename I>
+    util::array<int>
+    align_lines_verticaly(const Image<I>& input,
+			  util::array<box<mln_site(I)> >& lines_bboxes);
+
+
+# ifndef MLN_INCLUDE_ONLY
+
+
+    template <typename I>
+    util::array<int>
+    align_lines_verticaly(const Image<I>& input,
+			  util::array<box<mln_site(I)> >& lines_bboxes)
+    {
+      trace::entering("scribo::table::align_lines_horizontaly");
+
+      mln_precondition(exact(input).is_valid());
+      util::array<int> res = internal::align_lines(geom::ncols(input),
+						   geom::min_col(input),
+						   geom::max_col(input),
+						   lines_bboxes, 1);
+
+      trace::exiting("scribo::table::align_lines_horizontaly");
+      return res;
+    }
+
+
+# endif // ! MLN_INCLUDE_ONLY
+
+
+  } // end of namespace scribo::table
+
+} // end of namespace scribo
+
+#endif // ! SCRIBO_TABLE_ALIGN_LINES_VERTICALY_HH
diff --git a/milena/sandbox/scribo/table/connect_horizontal_lines.hh b/milena/sandbox/scribo/table/connect_horizontal_lines.hh
new file mode 100644
index 0000000..7dcf638
--- /dev/null
+++ b/milena/sandbox/scribo/table/connect_horizontal_lines.hh
@@ -0,0 +1,94 @@
+// 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.
+
+
+#ifndef SCRIBO_TABLE_CONNECT_HORIZONTAL_LINES_HH
+# define SCRIBO_TABLE_CONNECT_HORIZONTAL_LINES_HH
+
+/// \file scribo/table/connect_horizontal_lines.hh
+///
+/// Connect horizontal lines with the new aligned columns.
+
+# include <mln/core/concept/image.hh>
+# include <mln/util/array.hh>
+# include <mln/util/couple.hh>
+
+# include <scribo/table/internal/connect_lines.hh>
+
+
+namespace scribo
+{
+
+  namespace table
+  {
+
+
+    /// Connect horizontal lines with the new aligned columns.
+    ///
+    /// \param[in]	aligned_cols  a list of new aligned cols.
+    /// \param[in,out]	tableboxes    the vertical and horizontal lines
+    ///				      bounding boxes.
+    /// \param[in]	input	      The image from where the lines are
+    ///				      extracted.
+    template <typename I>
+    void
+    connect_vertical_lines(const util::array<int>& aligned_rows,
+			   util::couple<util::array<box<mln_site(I)> >,
+					util::array<box<mln_site(I)> > > tableboxes,
+			   const Image<I>& input)
+
+
+# ifndef MLN_INCLUDE_ONLY
+
+
+    template <typename I>
+    void
+    connect_horizontal_lines(const util::array<int>& aligned_cols,
+			     util::couple<util::array<box<mln_site(I)> >,
+					  util::array<box<mln_site(I)> > > tableboxes,
+			     const Image<I>& input)
+    {
+      trace::entering("scribo::table::connect_horizontal_lines");
+      mln_precondition(exact(input).is_valid());
+
+      internal::connect_lines(aligned_cols, tableboxes.second(),
+			      1, exact(input).ncols());
+
+      trace::exiting("scribo::table::connect_horizontal_lines");
+    }
+
+
+# endif // ! MLN_INCLUDE_ONLY
+
+
+  } // end of namespace scribo::table
+
+} // end of namespace scribo
+
+
+#endif // ! SCRIBO_TABLE_CONNECT_HORIZONTAL_LINES_HH
diff --git a/milena/sandbox/scribo/table/connect_vertical_lines.hh b/milena/sandbox/scribo/table/connect_vertical_lines.hh
new file mode 100644
index 0000000..98c39f7
--- /dev/null
+++ b/milena/sandbox/scribo/table/connect_vertical_lines.hh
@@ -0,0 +1,95 @@
+// 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.
+
+
+#ifndef SCRIBO_TABLE_CONNECT_VERTICAL_LINES_HH
+# define SCRIBO_TABLE_CONNECT_VERTICAL_LINES_HH
+
+/// \file scribo/table/connect_vertical_lines.hh
+///
+/// Connect vertical lines with aligned rows.
+
+# include <mln/core/concept/image.hh>
+# include <mln/util/array.hh>
+# include <mln/util/couple.hh>
+
+# include <scribo/table/internal/connect_lines.hh>
+
+
+namespace scribo
+{
+
+  namespace table
+  {
+
+
+    /// Connect vertical lines with the new aligned rows.
+    ///
+    /// \param[in]	aligned_rows  a list of new aligned rows.
+    /// \param[in,out]	tableboxes    the vertical and horizontal lines
+    ///				      bounding boxes.
+    /// \param[in]	input	      The image from where the lines are
+    ///				      extracted.
+    template <typename I>
+    void
+    connect_vertical_lines(const util::array<int>& aligned_rows,
+			   util::couple<util::array<box<mln_site(I)> >,
+					util::array<box<mln_site(I)> > > tableboxes,
+			   const Image<I>& input)
+
+
+# ifndef MLN_INCLUDE_ONLY
+
+
+    /// Connect vertical lines with the new aligned rows.
+    template <typename I>
+    void
+    connect_vertical_lines(const util::array<int>& aligned_rows,
+			   util::couple<util::array<box<mln_site(I)> >,
+					util::array<box<mln_site(I)> > > tableboxes,
+			   const Image<I>& input)
+    {
+      trace::entering("scribo::table::connect_vertical_lines");
+      mln_precondition(exact(input).is_valid());
+
+      internal::connect_lines(aligned_rows, tableboxes.first(),
+			      0, exact(input).nrows());
+
+      trace::exiting("scribo::table::connect_vertical_lines");
+    }
+
+
+# endif // ! MLN_INCLUDE_ONLY
+
+
+  } // end of namespace scribo::table
+
+} // end of namespace scribo
+
+
+#endif // ! SCRIBO_TABLE_CONNECT_VERTICAL_LINES_HH
diff --git a/milena/sandbox/scribo/table/erase.hh b/milena/sandbox/scribo/table/erase.hh
new file mode 100644
index 0000000..9d94edb
--- /dev/null
+++ b/milena/sandbox/scribo/table/erase.hh
@@ -0,0 +1,97 @@
+// 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.
+
+#ifndef SCRIBO_TABLE_ERASE_HH
+# define SCRIBO_TABLE_ERASE_HH
+
+/// \file scribo/table/erase.hh
+///
+/// Erase the table lines in an image.
+
+# include <mln/core/concept/image.hh>
+# include <mln/core/site_set/box.hh>
+# include <mln/core/routine/duplicate.hh>
+
+# include <mln/util/array.hh>
+# include <mln/util/couple.hh>
+
+# include <scribo/core/erase_bboxes.hh>
+
+namespace scribo
+{
+
+  namespace table
+  {
+
+    /// Erase table line bboxes from an image.
+    ///
+    /// \param[in]  line_bboxes   vertical and horizontal line bounding
+    ///				  boxes.
+    /// \param[in]  in		  A binary image from which the table line
+    ///				  bboxes are extracted.
+    ///
+    ///
+    ///	\return A copy of \p in where the table lines are removed.
+    template <typename I>
+    mln_concrete(I)
+    erase(const Image<I>& in,
+	  const util::couple<util::array< box<mln_site(I)> >,
+			     util::array< box<mln_site(I)> > >& line_bboxes);
+
+
+
+# ifndef MLN_INCLUDE_ONLY
+
+
+    template <typename I>
+    mln_concrete(I)
+    erase(const Image<I>& in,
+	  const util::couple<util::array< box<mln_site(I)> >,
+			     util::array< box<mln_site(I)> > >& line_bboxes)
+    {
+      trace::entering("scribo::internal::erase_table");
+      mlc_equal(mln_value(I),bool)::check();
+      mln_precondition(exact(in).is_valid());
+
+      I output = duplicate(in);
+
+      erase_bboxes(output, line_bboxes.first());
+      erase_bboxes(output, line_bboxes.second());
+
+      trace::exiting("scribo::internal::erase_table");
+      return output;
+    }
+
+
+# endif // ! MLN_INCLUDE_ONLY
+
+  } // end of namespace scribo::table
+
+} // end of namespace scribo
+
+#endif // ! SCRIBO_TABLE_ERASE_HH
diff --git a/milena/sandbox/scribo/table/extract_lines_with_rank.hh b/milena/sandbox/scribo/table/extract_lines_with_rank.hh
new file mode 100644
index 0000000..242dc30
--- /dev/null
+++ b/milena/sandbox/scribo/table/extract_lines_with_rank.hh
@@ -0,0 +1,127 @@
+// 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.
+
+
+#ifndef SCRIBO_TABLE_EXTRACT_LINES_WITH_RANK_HH
+# define SCRIBO_TABLE_EXTRACT_LINES_WITH_RANK_HH
+
+/// \file scribo/table/extract_lines_with_rank.hh
+///
+/// Extract table lines using a rank filter.
+
+
+# include <mln/core/concept/image.hh>
+# include <mln/core/concept/window.hh>
+# include <mln/core/concept/neighborhood.hh>
+# include <mln/core/site_set/box.hh>
+
+# include <mln/morpho/rank_filter.hh>
+
+# include <mln/accu/bbox.hh>
+
+# include <mln/util/array.hh>
+# include <mln/util/couple.hh>
+
+
+# include <scribo/core/component_bboxes.hh>
+
+namespace scribo
+{
+
+  namespace table
+  {
+
+    /// Find table bboxes thanks to a rank filter.
+    /*!
+     *
+     * \param[in] input_ A binary image.
+     * \param[in] nbh_ The neighborhood used for labeling image components.
+     * \param[in] label_type The type used to store the labels.
+     * \param[in] vwin Window used to extract the vertical lines in the rank
+     *		       filter.
+     * \param[in] hwin Window used to extract the horizontal lines in the rank
+     *		       filter.
+     * \param[in] vrank_k Rank used for vertical lines filtering.
+     * \param[in] hrank_k Rank used for horizontal lines filtering.
+     *
+     * \return pair of array of bounding boxes. The first array holds the
+     *		vertical lines bounding boxes and the second one the
+     *		horizontal lines bounding boxes.
+     */
+    template <typename I, typename N, typename V, typename HW, typename VW>
+    util::couple<util::array<box<mln_site(I)> >,
+		 util::array<box<mln_site(I)> > >
+    extract_lines_with_rank(const Image<I>& input_,
+			    const Neighborhood<N>& nbh_, const V& label_type,
+			    const Window<HW>& vwin, const Window<VW>& hwin,
+			    unsigned vrank_k, unsigned hrank_k);
+
+
+# ifndef MLN_INCLUDE_ONLY
+
+    template <typename I, typename N, typename V, typename HW, typename VW>
+    inline
+    util::couple<util::array<box<mln_site(I)> >,
+		 util::array<box<mln_site(I)> > >
+    extract_lines_with_rank(const Image<I>& input_,
+			    const Neighborhood<N>& nbh_, const V& label_type,
+			    const Window<HW>& vwin, const Window<VW>& hwin,
+			    unsigned vrank_k, unsigned hrank_k)
+    {
+      trace::entering("scribo::table::extract_lines_with_rank");
+
+      mlc_equal(mln_value(I),bool)::check();
+      mlc_is_a(V, mln::value::Symbolic)::check();
+      const I& input = exact(input_);
+      const N& nbh = exact(nbh_);
+      mln_precondition(input.is_valid());
+      mln_precondition(nbh.is_valid());
+      mln_precondition(exact(vwin).is_valid());
+      mln_precondition(exact(hwin).is_valid());
+
+      typedef accu::bbox<mln_psite(I)> A;
+      typedef util::array<mln_result(A)> boxes_t;
+
+      // Vertical lines
+      mln_ch_value(I,bool) vfilter = morpho::rank_filter(input, vwin, vrank_k);
+      boxes_t vboxes = component_bboxes(vfilter, nbh, label_type);
+
+      // Horizontal lines.
+      mln_ch_value(I,bool) hfilter = morpho::rank_filter(input, hwin, hrank_k);
+      boxes_t hboxes = component_bboxes(hfilter, nbh, label_type);
+
+      return mln::make::couple(vboxes, hboxes);
+    }
+
+# endif // !MLN_INCLUDE_ONLY
+
+  } // end of namespace scribo::table
+
+} // end of namespace scribo
+
+#endif // ! SCRIBO_TABLE_EXTRACT_LINES_WITH_RANK_HH
diff --git a/milena/sandbox/scribo/table/internal/align_lines.hh b/milena/sandbox/scribo/table/internal/align_lines.hh
new file mode 100644
index 0000000..aaad9fa
--- /dev/null
+++ b/milena/sandbox/scribo/table/internal/align_lines.hh
@@ -0,0 +1,183 @@
+// 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.
+
+
+#ifndef SCRIBO_TABLE_INTERNAL_ALIGN_LINES_HH
+# define SCRIBO_TABLE_INTERNAL_ALIGN_LINES_HH
+
+/// \file scribo/table/internal/align_lines.hh
+///
+/// Align table lines verticaly or horizontaly.
+
+
+# include <mln/core/site_set/box.hh>
+# include <mln/util/array.hh>
+# include <mln/util/set.hh>
+
+# include <scribo/core/macros.hh>
+
+
+namespace scribo
+{
+
+  namespace table
+  {
+
+    namespace internal
+    {
+
+      /// Align table lines bboxes according to a given dimension.
+      ///
+      /// \return A list of the resulting aligned cols. Each integer is actually
+      ///		a col number.
+      /*
+      **
+      **	  0 1 3 4 5 6
+      **	  ------------	  -------
+      **       0 |- - - - -   |	 | {0,1} |
+      **       1 |        - - |	 | {0,1} |
+      **       2 |            |	 | {1}	 |
+      **       3 |	      |	 |	 |
+      **       4 |	      |	 | {2}	 |
+      **       5 |- -	      |	 | {2}	 |
+      **       6 |	      |	 | {2}	 |
+      **
+      ** \p hboxes contains all the table lines bboxes. Each bbox is
+      ** associated with an id, its location in the array.
+      **
+      ** For each bbox, its id is marked in a vector. The location is defined,
+      ** according to the given parameter \p dim, either by the row or the col
+      ** value of the pmin site.
+      **
+      ** Ids are then propagated in the vector according a small delta value.
+      ** if bbox ids are at the same location in the vector, their related bboxes
+      ** are likely to be on the same line.
+      **
+      ** Finally, iterate over the vector until all bboxes have been treated.
+      ** For each iteration, the set with a specific number of elements is found
+      ** and all bboxes referenced in this set are aligned on the same row or col.
+      **
+      */
+      template <typename P>
+      util::array<int>
+      align_lines(unsigned nsites,
+		  int min_coord,
+		  int max_coord,
+		  util::array<box<P> >& line_boxes,
+		  unsigned dim);
+
+
+
+# ifndef MLN_INCLUDE_ONLY
+
+
+      template <typename P>
+      util::array<int>
+      align_lines(unsigned nsites,
+		  int min_coord,
+		  int max_coord,
+		  util::array<box<P> >& line_boxes,
+		  unsigned dim)
+      {
+	trace::entering("scribo::internal::align_lines");
+
+	mln_precondition(nsites > 0);
+
+	util::array< util::set<unsigned> > lines;
+	lines.resize(nsites);
+
+	// Map components with actual lines.
+	for_all_components(i, line_boxes)
+	{
+	  int minline = line_boxes[i].pmin()[dim] - 5;
+	  minline = (minline < min_coord ? min_coord : minline);
+	  int maxline = line_boxes[i].pmax()[dim] + 5;
+	  maxline = (maxline > max_coord ? max_coord : maxline);
+
+	  for (int line = minline;
+	      line <= maxline; ++line)
+	    lines[line].insert(i);
+	}
+
+	// Init box2line
+	util::array<int> box2line;
+	box2line.resize(line_boxes.nelements());
+	for_all_elements(i, box2line)
+	  box2line[i] = -1;
+
+	// Find the line with the highest element count.
+	unsigned max_nelts = 0;
+	for_all_elements(i, lines)
+	  if (max_nelts < lines[i].nelements())
+	    max_nelts = lines[i].nelements();
+
+	// Aligning lines
+	// FIXME: not optimal... Make it faster!
+	// We may do too much iterations (while loop) and some of them may
+	// be done for nothing...
+	util::array<int> newlines;
+	while (max_nelts > 0)
+	{
+	  for_all_elements(i, lines)
+	    if (lines[i].nelements() == max_nelts)
+	    {
+	      accu::mean<unsigned> mean;
+	      for_all_elements(j, lines[i])
+		if (box2line[lines[i][j]] == -1)
+		  mean.take(line_boxes[lines[i][j]].center()[dim]);
+
+	      if (mean.is_valid())
+	      {
+		for_all_elements(j, lines[i])
+		  if (box2line[lines[i][j]] == -1)
+		  {
+		    line_boxes[lines[i][j]].pmin()[dim] = mean.to_result();
+		    line_boxes[lines[i][j]].pmax()[dim] = mean.to_result();
+		    box2line[lines[i][j]] = mean.to_result();
+		  }
+		newlines.append(mean.to_result());
+	      }
+	    }
+	  --max_nelts;
+	}
+
+	trace::exiting("scribo::internal::align_lines");
+	return newlines;
+      }
+
+
+# endif // ! MLN_INCLUDE_ONLY
+
+
+    } // end of namespace scribo::table::internal
+
+  } // end of namespace scribo::table
+
+} // end of namespace scribo
+
+#endif // ! SCRIBO_TABLE_INTERNAL_ALIGN_LINES_HH
diff --git a/milena/sandbox/scribo/table/internal/connect_lines.hh b/milena/sandbox/scribo/table/internal/connect_lines.hh
new file mode 100644
index 0000000..d510fe2
--- /dev/null
+++ b/milena/sandbox/scribo/table/internal/connect_lines.hh
@@ -0,0 +1,118 @@
+// 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.
+
+
+#ifndef SCRIBO_TABLE_INTERNAL_CONNECT_LINES_HH
+# define SCRIBO_TABLE_INTERNAL_CONNECT_LINES_HH
+
+/// \file scribo/table/connect_vertical_lines.hh
+///
+/// Connect vertical lines with aligned rows.
+
+# include <mln/core/image/image1d.hh>
+
+# include <mln/data/fill.hh>
+
+# include <mln/morpho/elementary/dilation.hh>
+# include <mln/util/array.hh>
+# include <mln/util/couple.hh>
+
+# include <mln/opt/at.hh>
+
+# include <scribo/core/macros.hh>
+
+
+namespace scribo
+{
+
+  namespace table
+  {
+
+    namespace internal
+    {
+
+      /// Connect vertical and horizontal lines if they are close to each other.
+      ///
+      ///  ------                 ------
+      ///		  --->          |
+      ///        |                      |
+      ///        |                      |
+      ///
+      ///
+      /// FIXME: doc arguments.
+      template <typename P>
+      void
+      connect_lines(const util::array<int>& aligned_lines,
+		    util::array< box<P> >& boxes,
+		    unsigned dim,
+		    unsigned dim_size);
+
+
+# ifndef MLN_INCLUDE_ONLY
+
+
+      template <typename P>
+      void
+      connect_lines(const util::array<int>& aligned_lines,
+		    util::array< box<P> >& boxes,
+		    unsigned dim,
+		    unsigned dim_size)
+      {
+	trace::entering("scribo::table::internal::connect_lines");
+
+	image1d<int> l(dim_size);
+	data::fill(l, -1);
+
+	for_all_elements(i, aligned_lines)
+	  opt::at(l, aligned_lines[i]) = i;
+
+	for (unsigned i = 0; i < settings.max_dist_lines; ++i)
+	  l = morpho::elementary::dilation(l, c2());
+
+	for_all_components(i, boxes)
+	{
+	  util::couple<point2d, point2d> cp = central_sites(boxes[i], dim);
+	  if (opt::at(l, cp.first()[dim]) != -1)
+	    boxes[i].pmin()[dim] = aligned_lines[opt::at(l, cp.first()[dim])];
+	  if (opt::at(l, cp.second()[dim]) != -1)
+	    boxes[i].pmax()[dim] = aligned_lines[opt::at(l, cp.second()[dim])];
+	}
+
+	trace::exiting("scribo::table::internal::connect_lines");
+      }
+
+
+# endif // ! MLN_INCLUDE_ONLY
+
+
+  } // end of namespace scribo::table
+
+} // end of namespace scribo
+
+
+#endif // ! SCRIBO_TABLE_INTERNAL_CONNECT_LINES_HH
diff --git a/milena/sandbox/scribo/table/internal/repair_lines.hh b/milena/sandbox/scribo/table/internal/repair_lines.hh
new file mode 100644
index 0000000..87f0933
--- /dev/null
+++ b/milena/sandbox/scribo/table/internal/repair_lines.hh
@@ -0,0 +1,132 @@
+// 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.
+
+#ifndef SCRIBO_TABLE_INTERNAL_REPAIR_LINES_HH
+# define SCRIBO_TABLE_INTERNAL_REPAIR_LINES_HH
+
+/// \file scribo/table/internal/repair_lines.hh
+///
+/// Repair lines which have small discontinuities.
+
+# include <vector>
+
+# include <mln/core/concept/image.hh>
+# include <mln/core/image/extension_val.hh>
+# include <mln/core/site_set/box.hh>
+# include <mln/data/fill.hh>
+# include <mln/util/couple.hh>
+# include <mln/util/array.hh>
+# include <mln/win/line.hh>
+# include <mln/pw/all.hh>
+
+# include <scribo/core/central_sites.hh>
+# include <scribo/table/internal/repair_lines.hh>
+
+
+namespace scribo
+{
+
+  namespace table
+  {
+
+    namespace internal
+    {
+
+
+# ifndef MLN_INCLUDE_ONLY
+
+      /// Repair lines which have small discontinuities.
+      /// FIXME: buggy. Sometimes few lines move or shrink!
+      template <unsigned axis, typename I>
+      void
+      repair_lines(const Image<I>& input_,
+		   util::array<box<mln_site(I)> >& tableboxes)
+      {
+	trace::entering("scribo::table::internal::repair_lines");
+
+	const I& input = exact(input_);
+	mln_precondition(input.is_valid());
+
+	typedef mln_site(I) P;
+	typedef win::line<mln_grid(P), axis, mln_coord(P)> line_t;
+
+	// Initialization
+	mln_ch_value(I,unsigned) l(input.domain());
+	data::fill(l, literal::zero);
+	for_all_components(i, tableboxes)
+	{
+	  util::couple<P,P> cp = central_sites(tableboxes[i], axis);
+	  l(cp.first()) = i;
+	  l(cp.second()) = i;
+	}
+
+	// Repair
+	extension_val<mln_ch_value(I,unsigned)> l_ext(l, literal::zero);
+
+	util::array<box<P> > result;
+	std::vector<bool> to_keep(tableboxes.nelements(), true);
+
+	mln_VAR(tbb_ima, extend(l | pw::value(l) != literal::zero, l));
+	//FIXME: use a half window, just the bottom of the vertical line.
+	line_t vl(settings.repair_max_dist);
+	mln_piter(tbb_ima_t) p(tbb_ima.domain());
+	mln_qiter(line_t) q(vl, p);
+	for_all(p)
+	  for_all(q)
+	  if (l_ext(q) != literal::zero && l_ext(q) != l_ext(p))
+	  {
+	    to_keep[l_ext(q)] = false;
+
+	    tableboxes[l_ext(p)].pmax() = tableboxes[l_ext(q)].pmax();
+
+	    util::couple<P,P> cp = central_sites(tableboxes[l_ext(q)], axis);
+	    l_ext(cp.first()) = l_ext(p);
+	    l_ext(cp.second()) = l_ext(p);
+	  }
+
+
+	// Remove merged boxes.
+	for_all_elements(i, tableboxes)
+	  if (to_keep[i])
+	    result.append(tableboxes[i]);
+
+	tableboxes = result;
+	trace::exiting("scribo::table::internal::repair_lines");
+      }
+
+
+# endif // ! MLN_INCLUDE_ONLY
+
+    } // end of namespace scribo::table::internal
+
+  } // end of namespace scribo::table
+
+} // end of namespace scribo
+
+
+#endif // ! SCRIBO_TABLE_INTERNAL_REPAIR_LINES_HH
diff --git a/milena/sandbox/scribo/table/repair_horizontal_lines.hh b/milena/sandbox/scribo/table/repair_horizontal_lines.hh
new file mode 100644
index 0000000..7c1edbd
--- /dev/null
+++ b/milena/sandbox/scribo/table/repair_horizontal_lines.hh
@@ -0,0 +1,92 @@
+// 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.
+
+#ifndef SCRIBO_TABLE_REPAIR_HORIZONTAL_LINES_HH
+# define SCRIBO_TABLE_REPAIR_HORIZONTAL_LINES_HH
+
+/// \file scribo/table/repair_horizontal_lines.hh
+///
+/// Repair horizontal lines which have small discontinuities.
+
+# include <mln/core/concept/image.hh>
+# include <mln/util/couple.hh>
+# include <mln/util/array.hh>
+
+# include <scribo/table/internal/repair_lines.hh>
+
+namespace scribo
+{
+
+  namespace table
+  {
+
+
+    /// Repair horizontal lines which have small discontinuities.
+    /*!
+     *    ----  ----	==>	----------
+     *
+     *	\param[in] input	      Image from which the table bounding
+     *				      boxes are extracted.
+     *	\param[in,out] tablesboxes    Table line bounding boxes.
+     *	\param[in] max_discontinuity  Repair discontinuity which are smaller
+     *				      than this value.
+     */
+    template <typename I>
+    void
+    repair_horizontal_lines(const Image<I>& input,
+			  util::couple<util::array<box<mln_site(I)> >,
+				       util::array<box<mln_site(I)> > >& tableboxes,
+			  unsigned max_discontinuity);
+
+# ifndef MLN_INCLUDE_ONLY
+
+    template <typename I>
+    inline
+    void
+    repair_horizontal_lines(const Image<I>& input,
+			    util::couple<util::array<box<mln_site(I)> >,
+					 util::array<box<mln_site(I)> > >& tableboxes,
+			    unsigned max_discontinuity)
+    {
+      trace::entering("scribo::table::repair_horizontal_lines");
+      mln_precondition(exact(input).is_valid());
+
+      internal::repair_lines<1>(input, tableboxes.second(), max_discontinuity);
+
+      trace::exiting("scribo::table::repair_horizontal_lines");
+    }
+
+
+# endif // ! MLN_INCLUDE_ONLY
+
+  } // end of namespace scribo::table
+
+} // end of namespace scribo
+
+
+#endif // ! SCRIBO_TABLE_REPAIR_HORIZONTAL_LINES_HH
diff --git a/milena/sandbox/scribo/table/repair_vertical_lines.hh b/milena/sandbox/scribo/table/repair_vertical_lines.hh
new file mode 100644
index 0000000..94ae3f1
--- /dev/null
+++ b/milena/sandbox/scribo/table/repair_vertical_lines.hh
@@ -0,0 +1,98 @@
+// 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.
+
+#ifndef SCRIBO_TABLE_REPAIR_VERTICAL_LINES_HH
+# define SCRIBO_TABLE_REPAIR_VERTICAL_LINES_HH
+
+/// \file scribo/table/repair_vertical_lines.hh
+///
+/// Repair vertical lines which have small discontinuities.
+
+# include <mln/core/concept/image.hh>
+# include <mln/util/couple.hh>
+# include <mln/util/array.hh>
+
+# include <scribo/table/internal/repair_lines.hh>
+
+namespace scribo
+{
+
+  namespace table
+  {
+
+
+    /// Repair vertical lines which have small discontinuities.
+    /*!
+     *
+     *	  |	  | \n
+     *	  |	  | \n
+     *	     ==>  | \n
+     *	  |	  | \n
+     *	  |	  | \n
+     *
+     *
+     *	\param[in] input	      Image from which the table bounding
+     *				      boxes are extracted.
+     *	\param[in,out] tablesboxes    Table line bounding boxes.
+     *	\param[in] max_discontinuity  Repair discontinuity which are smaller
+     *				      than this value.
+     */
+    template <typename I>
+    void
+    repair_vertical_lines(const Image<I>& input,
+			  util::couple<util::array<box<mln_site(I)> >,
+				       util::array<box<mln_site(I)> > >& tblboxes,
+			  unsigned max_discontinuity);
+
+# ifndef MLN_INCLUDE_ONLY
+
+    template <typename I>
+    inline
+    void
+    repair_vertical_lines(const Image<I>& input,
+			  util::couple<util::array<box<mln_site(I)> >,
+				       util::array<box<mln_site(I)> > >& tblboxes,
+			  unsigned max_discontinuity)
+    {
+      trace::entering("scribo::table::repair_vertical_lines");
+      mln_precondition(exact(input).is_valid());
+
+      internal::repair_lines<0>(input, tblboxes.first(), max_discontinuity);
+
+      trace::exiting("scribo::table::repair_vertical_lines");
+    }
+
+
+# endif // ! MLN_INCLUDE_ONLY
+
+  } // end of namespace scribo::table
+
+} // end of namespace scribo
+
+
+#endif // ! SCRIBO_TABLE_REPAIR_VERTICAL_LINES_HH
diff --git a/milena/sandbox/scribo/text/extract_bboxes.hh b/milena/sandbox/scribo/text/extract_bboxes.hh
new file mode 100644
index 0000000..0c88880
--- /dev/null
+++ b/milena/sandbox/scribo/text/extract_bboxes.hh
@@ -0,0 +1,95 @@
+// 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.
+
+
+#ifndef SCRIBO_TEXT_GROUPING_EXTRACT_BBOXES_HH
+# define SCRIBO_TEXT_GROUPING_EXTRACT_BBOXES_HH
+
+/// \file scribo/text/grouping/group_with_single_link.hh
+///
+/// Extract text bounding boxes from a binary image.
+
+# include <mln/core/concept/image.hh>
+# include <mln/core/concept/neighborhood.hh>
+# include <mln/core/site_set/box.hh>
+# include <mln/labeling/blobs.hh>
+# include <mln/labeling/compute.hh>
+# include <mln/util/array.hh>
+
+# include <scribo/core/component_bboxes.hh>
+
+namespace scribo
+{
+
+  namespace text
+  {
+
+    using namespace mln;
+
+    /// Extract text bounding boxes from a binary image.
+    ///
+    /// \param[in] input_ A binary image.
+    ///
+    /// \return an array of bounding boxes. The first bounding box is
+    /// the background's.
+    template <typename I, typename N, typename V>
+    inline
+    util::array< box<mln_site(I)> >
+    extract_bboxes(const Image<I>& input_,
+		   const Neighborhood<N>& nbh, V& nbboxes);
+
+# ifndef MLN_INCLUDE_ONLY
+
+
+    template <typename I, typename N, typename V>
+    inline
+    util::array< box<mln_site(I)> >
+    extract_bboxes(const Image<I>& input_,
+		   const Neighborhood<N>& nbh, V& nbboxes)
+    {
+      trace::entering("scribo::text::extract_bboxes");
+
+      const I& input = exact(input_);
+
+      mlc_equal(mln_value(I), bool)::check();
+      mln_precondition(input.is_valid());
+
+      typedef util::array< box<mln_site(I)> > bboxes_t;
+      bboxes_t bboxes = component_bboxes(input, nbh, nbboxes);
+
+      trace::exiting("scribo::text::extract_bboxes");
+      return bboxes;
+    }
+
+# endif // ! MLN_INCLUDE_ONLY
+
+  } // end of namespace scribo::text
+
+} // end of namespace scribo
+
+#endif // ! SCRIBO_TEXT_GROUPING_EXTRACT_BBOXES_HH
diff --git a/milena/sandbox/scribo/text/grouping/group_from_double_link.hh b/milena/sandbox/scribo/text/grouping/group_from_double_link.hh
new file mode 100644
index 0000000..d178a80
--- /dev/null
+++ b/milena/sandbox/scribo/text/grouping/group_from_double_link.hh
@@ -0,0 +1,108 @@
+// 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.
+
+
+#ifndef SCRIBO_TEXT_GROUPING_GROUP_FROM_DOUBLE_LINK_HH
+# define SCRIBO_TEXT_GROUPING_GROUP_FROM_DOUBLE_LINK_HH
+
+/// \file scribo/text/grouping/group_from_double_link.hh
+///
+/// Link text bounding boxes with their left neighbor.
+
+# include <mln/core/concept/image.hh>
+# include <mln/core/site_set/box.hh>
+
+# include <mln/accu/bbox.hh>
+
+# include <mln/util/array.hh>
+
+# include <scribo/text/grouping/internal/find_root.hh>
+
+# include <scribo/core/macros.hh>
+
+
+namespace scribo
+{
+
+  namespace text
+  {
+
+    namespace grouping
+    {
+
+      /// FIXME: Add much more doc!
+      template <typename P>
+      util::array< box<P> >
+      group_from_double_link(const util::array< box<P> >& textbboxes,
+			     util::array<unsigned>& left_link,
+			     util::array<unsigned>& right_link);
+
+# ifndef MLN_INCLUDE_ONLY
+
+      template <typename P>
+      inline
+      util::array< box<P> >
+      group_from_double_link(const util::array< box<P> >& textbboxes,
+			     util::array<unsigned>& left_link,
+			     util::array<unsigned>& right_link)
+      {
+	trace::entering("scribo::text::grouping::group_from_double_link");
+
+	mln_precondition(left_link.nelements() == right_link.nelements());
+
+	util::array< accu::bbox<P> > tboxes;
+	tboxes.resize(textbboxes.nelements());
+	for_all_components(i, textbboxes)
+	{
+	  unsigned nbh = right_link[left_link[i]];
+	  if (nbh == i)
+	    tboxes[left_link[i]].take(textbboxes[i]);
+	  else
+	    tboxes[i].take(textbboxes[i]);
+	}
+
+	util::array< box<P> > result;
+	// component 0, the background, has an invalid box.
+	result.append(box<P>());
+	for_all_components(i, tboxes)
+	  if (tboxes[i].is_valid())
+	    result.append(tboxes[i]);
+
+	trace::exiting("scribo::text::grouping::group_from_double_link");
+	return result;
+      }
+
+# endif // ! MLN_INCLUDE_ONLY
+
+    } // end of namespace scribo::text::grouping
+
+  } // end of namespace scribo::text
+
+} // end of namespace scribo
+
+#endif // ! SCRIBO_TEXT_GROUPING_GROUP_FROM_DOUBLE_LINK_HH
diff --git a/milena/sandbox/scribo/text/grouping/group_from_multiple_links.hh b/milena/sandbox/scribo/text/grouping/group_from_multiple_links.hh
new file mode 100644
index 0000000..612fb87
--- /dev/null
+++ b/milena/sandbox/scribo/text/grouping/group_from_multiple_links.hh
@@ -0,0 +1,154 @@
+// Copyright (C) 2009 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, 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.
+
+#ifndef SCRIBO_TEXT_GROUPING_GROUP_FROM_MULTIPLE_LINKS_HH
+# define SCRIBO_TEXT_GROUPING_GROUP_FROM_MULTIPLE_LINKS_HH
+
+/// \file scribo/text/grouping/group_from_multiple_links.hh
+///
+/// Link text bounding boxes with their neighbors.
+
+# include <mln/core/concept/graph.hh>
+# include <mln/core/concept/image.hh>
+# include <mln/core/site_set/box.hh>
+
+# include <mln/canvas/browsing/depth_first_search.hh>
+
+# include <mln/accu/bbox.hh>
+
+# include <mln/pw/all.hh>
+
+# include <mln/fun/i2v/array.hh>
+
+# include <mln/util/array.hh>
+
+# include <scribo/core/macros.hh>
+
+namespace scribo
+{
+
+  namespace text
+  {
+
+    namespace grouping
+    {
+
+      /// FIXME: Add much more doc!
+      template <typename G, typename P>
+      util::array<box<P> >
+      group_from_multiple_links(util::array<box<P> >& textbboxes,
+				const Graph<G>& g_);
+
+# ifndef MLN_INCLUDE_ONLY
+
+
+      namespace internal
+      {
+
+	/// Functor to be passed to depth_first_search.
+	/// Map each component vertex with its representative vertex id.
+	struct map_vertex_to_component_id_functor
+	{
+	    template <typename G>
+	    void init(const Graph<G>& g)
+	    {
+	      vertextocomp.resize(exact(g).v_nmax(), mln_max(unsigned));
+	      ncomp = 0;
+	    }
+
+	  void final()
+	  {}
+
+	  void next_component()
+	  { ++ncomp; }
+
+	  void new_component_from_vertex(unsigned id)
+	  { vertextocomp(id) = ncomp; }
+
+	  void added_to_queue(unsigned id)
+	  { new_component_from_vertex(id); }
+
+	  void process_vertex(unsigned)
+	  {}
+
+	  bool to_be_treated(unsigned id)
+	  { return vertextocomp(id) == mln_max(unsigned); }
+
+	  bool to_be_queued(unsigned id)
+	  { return to_be_treated(id); }
+
+	  unsigned ncomp;
+	  fun::i2v::array<unsigned> vertextocomp;
+	};
+
+      } // end of namespace scribo::text::grouping::internal
+
+
+
+      template <typename G, typename P>
+      inline
+      util::array<box<P> >
+      group_from_multiple_links(util::array<box<P> >& textbboxes,
+				const Graph<G>& g_)
+      {
+	trace::entering("scribo::text::grouping::group_from_multiple_links");
+
+	const G& g = exact(g_);
+
+	mln_assertion(g.is_valid());
+
+	internal::map_vertex_to_component_id_functor f;
+	canvas::browsing::depth_first_search(g, f);
+
+	util::array< accu::bbox<P> > tboxes;
+	tboxes.resize(textbboxes.nelements());
+	for_all_components(i, textbboxes)
+	  tboxes[f.vertextocomp(i)].take(textbboxes[i]);
+
+	util::array< box<P> > result;
+	// Component 0 - the background has not valid bboxes.
+	result.append(box<P>());
+	for_all_components(i, textbboxes)
+	  if (tboxes[i].is_valid())
+	    result.append(tboxes[i].to_result());
+
+	mln_assertion(result.nelements() == f.ncomp);
+
+	trace::exiting("scribo::text::grouping::group_from_multiple_links");
+	return result;
+      }
+
+
+# endif // ! MLN_INCLUDE_ONLY
+
+    } // end of namespace scribo::text::grouping
+
+  } // end of namespace scribo::text
+
+} // end of namespace scribo
+
+#endif // ! SCRIBO_TEXT_GROUPING_GROUP_FROM_MULTIPLE_LINKS_HH
diff --git a/milena/sandbox/scribo/text/grouping/group_from_single_link.hh b/milena/sandbox/scribo/text/grouping/group_from_single_link.hh
new file mode 100644
index 0000000..eccde96
--- /dev/null
+++ b/milena/sandbox/scribo/text/grouping/group_from_single_link.hh
@@ -0,0 +1,101 @@
+// 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.
+
+
+#ifndef SCRIBO_TEXT_GROUPING_GROUP_FROM_SINGLE_LINK_HH
+# define SCRIBO_TEXT_GROUPING_GROUP_FROM_SINGLE_LINK_HH
+
+/// \file scribo/text/grouping/group_from_single_link.hh
+///
+/// Link text bounding boxes with their left neighbor.
+
+# include <mln/core/concept/image.hh>
+# include <mln/core/site_set/box.hh>
+
+# include <mln/accu/bbox.hh>
+
+# include <mln/util/array.hh>
+
+# include <scribo/text/grouping/internal/find_root.hh>
+
+# include <scribo/core/macros.hh>
+
+
+namespace scribo
+{
+
+  namespace text
+  {
+
+    namespace grouping
+    {
+
+      /// FIXME: Add much more doc!
+      template <typename P>
+      util::array< box<P> >
+      group_from_single_link(const util::array< box<P> >& textbboxes,
+			     util::array<unsigned>& link_array);
+
+# ifndef MLN_INCLUDE_ONLY
+
+      template <typename P>
+      inline
+      util::array< box<P> >
+      group_from_single_link(const util::array< box<P> >& textbboxes,
+			     util::array<unsigned>& link_array)
+      {
+	trace::entering("scribo::text::grouping::group_from_single_link");
+
+	for (unsigned i = 0; i < link_array.nelements(); ++i)
+	  link_array[i] = internal::find_root(link_array, i);
+
+	util::array< accu::bbox<P> > tboxes;
+	tboxes.resize(textbboxes.nelements());
+	for_all_components(i, textbboxes)
+	  tboxes[link_array[i]].take(textbboxes[i]);
+
+	util::array< box<P> > result;
+	// component 0, the background, has an invalid box.
+	result.append(box<P>());
+	for_all_components(i, tboxes)
+	  if (tboxes[i].is_valid())
+	    result.append(tboxes[i]);
+
+	trace::exiting("scribo::text::grouping::group_from_single_link");
+	return result;
+      }
+
+# endif // ! MLN_INCLUDE_ONLY
+
+    } // end of namespace scribo::text::grouping
+
+  } // end of namespace scribo::text
+
+} // end of namespace scribo
+
+#endif // ! SCRIBO_TEXT_GROUPING_GROUP_FROM_SINGLE_LINK_HH
diff --git a/milena/sandbox/scribo/text/grouping/group_with_multiple_links.hh b/milena/sandbox/scribo/text/grouping/group_with_multiple_links.hh
new file mode 100644
index 0000000..2f769fc
--- /dev/null
+++ b/milena/sandbox/scribo/text/grouping/group_with_multiple_links.hh
@@ -0,0 +1,115 @@
+// 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.
+
+
+#ifndef SCRIBO_TEXT_GROUPING_GROUP_WITH_MULTIPLE_LINKS_HH
+# define SCRIBO_TEXT_GROUPING_GROUP_WITH_MULTIPLE_LINKS_HH
+
+/// \file scribo/text/grouping/group_with_multiple_links.hh
+///
+/// Group character bounding boxes with multiple links.
+
+# include <mln/core/concept/image.hh>
+
+# include <mln/math/abs.hh>
+
+# include <mln/util/array.hh>
+
+# include <scribo/core/macros.hh>
+# include <scribo/text/grouping/internal/init_link_array.hh>
+# include <scribo/text/grouping/internal/update_link_graph.hh>
+
+namespace scribo
+{
+
+  namespace text
+  {
+
+    namespace grouping
+    {
+
+      /// Group character bounding boxes with multiple links.
+      /// Look up for neighbors on the left of each box.
+      template <typename I, typename N, typename V>
+      util::graph
+      group_with_multiple_links(const Image<I>& input_,
+				const Neighborhood<N>& nbh, V& nbboxes,
+				const util::array<box<mln_site(I)> >& textbboxes,
+				unsigned neighb_max_distance);
+
+# ifndef MLN_INCLUDE_ONLY
+
+      template <typename I, typename N, typename V>
+      util::graph
+      group_with_multiple_links(const Image<I>& input_,
+				const Neighborhood<N>& nbh, V& nbboxes,
+				const util::array<box<mln_site(I)> >& textbboxes,
+				unsigned neighb_max_distance)
+      {
+	trace::entering("scribo::text::grouping::group_with_multiple_links");
+
+	const I& input = exact(input_);
+
+	mlc_equal(mln_value(I), bool)::check();
+	mln_assertion(input.is_valid());
+
+	typedef mln_ch_value(I,V) lbl_t;
+	lbl_t lbl = labeling::blobs(input, nbh, nbboxes);
+
+	util::graph g(nbboxes.next());
+
+	for_all_ncomponents(i, nbboxes)
+	{
+	  unsigned midcol = (textbboxes[i].pmax().col()
+				- textbboxes[i].pmin().col()) / 2;
+	  int dmax = midcol + neighb_max_distance;
+	  mln_site(I) c = textbboxes[i].center();
+
+	  /// First site on the right of the central site
+	  mln_site(I) p = c + right;
+
+	  while (lbl.domain().has(p) && (lbl(p) == literal::zero || lbl(p) == i)
+	      && math::abs(p.col() - c.col()) < dmax)
+	    ++p.col();
+
+	  internal::update_link_graph(lbl, g, p, c, i, dmax);
+	}
+
+	trace::exiting("scribo::text::grouping::group_with_multiple_links");
+	return g;
+      }
+
+# endif // ! MLN_INCLUDE_ONLY
+
+    } // end of namespace scribo::text::grouping
+
+  } // end of namespace scribo::text
+
+} // end of namespace scribo
+
+#endif // ! SCRIBO_TEXT_GROUPING_GROUP_WITH_MULTIPLE_LINKS_HH
diff --git a/milena/sandbox/scribo/text/grouping/group_with_single_left_link.hh b/milena/sandbox/scribo/text/grouping/group_with_single_left_link.hh
new file mode 100644
index 0000000..f1a3013
--- /dev/null
+++ b/milena/sandbox/scribo/text/grouping/group_with_single_left_link.hh
@@ -0,0 +1,135 @@
+// 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.
+
+
+#ifndef SCRIBO_TEXT_GROUPING_GROUP_WITH_SINGLE_LEFT_LINK_HH
+# define SCRIBO_TEXT_GROUPING_GROUP_WITH_SINGLE_LEFT_LINK_HH
+
+/// \file scribo/text/grouping/group_with_single_left_link.hh
+///
+/// Link text bounding boxes with their left neighbor.
+///
+/// Merge code with text::grouping::group_with_single_right_link.hh
+
+# include <mln/core/concept/image.hh>
+# include <mln/core/concept/neighborhood.hh>
+
+# include <mln/math/abs.hh>
+
+# include <mln/util/array.hh>
+
+# include <scribo/core/macros.hh>
+# include <scribo/text/grouping/internal/init_link_array.hh>
+# include <scribo/text/grouping/internal/update_link_array.hh>
+
+//FIXME: not generic.
+# include <mln/core/alias/dpoint2d.hh>
+
+namespace scribo
+{
+
+  namespace text
+  {
+
+    namespace grouping
+    {
+
+      /// Map each character bounding box to its left bounding box neighbor
+      /// if possible.
+      /// Iterate to the right but link boxes to the left.
+      ///
+      /// \return an util::array. Map a bounding box to its left neighbor.
+      template <typename I, typename N, typename V>
+      inline
+      util::array<unsigned>
+      group_with_single_left_link(const Image<I>& input_,
+				  const Neighborhood<N>& nbh, V& nbboxes,
+				  const util::array< box<mln_site(I)> >& textbboxes,
+				  unsigned neighb_max_distance);
+
+# ifndef MLN_INCLUDE_ONLY
+
+      template <typename I, typename N, typename V>
+      inline
+      util::array<unsigned>
+      group_with_single_left_link(const Image<I>& input_,
+				  const Neighborhood<N>& nbh_, V& nbboxes,
+				  const util::array< box<mln_site(I)> >& textbboxes,
+				  unsigned neighb_max_distance)
+      {
+	trace::entering("scribo::text::grouping::group_with_single_left_link");
+
+	const I& input = exact(input_);
+	const N& nbh = exact(nbh_);
+
+        mlc_equal(mln_value(I), bool)::check();
+	mln_assertion(input.is_valid());
+	mln_precondition(nbh.is_valid());
+
+	typedef mln_ch_value(I,V) lbl_t;
+	lbl_t lbl = labeling::blobs(input, nbh, nbboxes);
+
+	util::array<unsigned> left_link(nbboxes.next());
+	internal::init_link_array(left_link);
+
+	for_all_ncomponents(i, nbboxes)
+	{
+	  unsigned midcol = (textbboxes[i].pmax().col()
+				- textbboxes[i].pmin().col()) / 2;
+	  int dmax = midcol + neighb_max_distance;
+	  mln_site(I) c = textbboxes[i].center();
+
+	  ///
+	  /// Find a neighbor on the right
+	  ///
+
+	  ///FIXME: the following code is not generic...
+	  /// First site on the right of the central site
+	  mln_site(I) p = c + right;
+
+	  while (lbl.domain().has(p) && (lbl(p) == literal::zero || lbl(p) == i)
+	      && math::abs(p.col() - c.col()) < dmax)
+	    ++p.col();
+
+	  internal::update_link_array(lbl, left_link, p, c, i, dmax);
+
+	}
+
+	trace::exiting("scribo::text::grouping::group_with_single_left_link");
+	return left_link;
+      }
+
+# endif // ! MLN_INCLUDE_ONLY
+
+    } // end of namespace scribo::text::grouping
+
+  } // end of namespace scribo::text
+
+} // end of namespace scribo
+
+#endif // ! SCRIBO_TEXT_GROUPING_GROUP_WITH_SINGLE_LEFT_LINK_HH
diff --git a/milena/sandbox/scribo/text/grouping/group_with_single_right_link.hh b/milena/sandbox/scribo/text/grouping/group_with_single_right_link.hh
new file mode 100644
index 0000000..7f1eeac
--- /dev/null
+++ b/milena/sandbox/scribo/text/grouping/group_with_single_right_link.hh
@@ -0,0 +1,135 @@
+// 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.
+
+
+#ifndef SCRIBO_TEXT_GROUPING_GROUP_WITH_SINGLE_RIGHT_LINK_HH
+# define SCRIBO_TEXT_GROUPING_GROUP_WITH_SINGLE_RIGHT_LINK_HH
+
+/// \file scribo/text/grouping/group_with_single_right_link.hh
+///
+/// Link text bounding boxes with their right neighbor.
+///
+/// Merge code with text::grouping::group_with_single_right_link.hh
+
+# include <mln/core/concept/image.hh>
+# include <mln/core/concept/neighborhood.hh>
+
+# include <mln/math/abs.hh>
+
+# include <mln/util/array.hh>
+
+# include <scribo/core/macros.hh>
+# include <scribo/text/grouping/internal/init_link_array.hh>
+# include <scribo/text/grouping/internal/update_link_array.hh>
+
+//FIXME: not generic.
+# include <mln/core/alias/dpoint2d.hh>
+
+namespace scribo
+{
+
+  namespace text
+  {
+
+    namespace grouping
+    {
+
+      /// Map each character bounding box to its right bounding box neighbor
+      /// if possible.
+      /// Iterate to the right but link boxes to the right.
+      ///
+      /// \return an util::array. Map a bounding box to its right neighbor.
+      template <typename I, typename N, typename V>
+      inline
+      util::array<unsigned>
+      group_with_single_right_link(const Image<I>& input_,
+				   const Neighborhood<N>& nbh, V& nbboxes,
+				   const util::array< box<mln_site(I)> >& textbboxes,
+				   unsigned neighb_max_distance);
+
+# ifndef MLN_INCLUDE_ONLY
+
+      template <typename I, typename N, typename V>
+      inline
+      util::array<unsigned>
+      group_with_single_right_link(const Image<I>& input_,
+				   const Neighborhood<N>& nbh_, V& nbboxes,
+				   const util::array< box<mln_site(I)> >& textbboxes,
+				   unsigned neighb_max_distance)
+      {
+	trace::entering("scribo::text::grouping::group_with_single_right_link");
+
+	const I& input = exact(input_);
+	const N& nbh = exact(nbh_);
+
+        mlc_equal(mln_value(I), bool)::check();
+	mln_assertion(input.is_valid());
+	mln_precondition(nbh.is_valid());
+
+	typedef mln_ch_value(I,V) lbl_t;
+	lbl_t lbl = labeling::blobs(input, nbh, nbboxes);
+
+	util::array<unsigned> right_link(nbboxes.next());
+	internal::init_link_array(right_link);
+
+	for_all_ncomponents(i, nbboxes)
+	{
+	  unsigned midcol = (textbboxes[i].pmax().col()
+				- textbboxes[i].pmin().col()) / 2;
+	  int dmax = midcol + neighb_max_distance;
+	  mln_site(I) c = textbboxes[i].center();
+
+	  ///
+	  /// Find a neighbor on the left
+	  ///
+
+	  ///FIXME: the following code is not generic...
+	  /// First site on the left of the central site
+	  mln_site(I) p = c + left;
+
+	  while (lbl.domain().has(p) && (lbl(p) == literal::zero || lbl(p) == i)
+	      && math::abs(p.col() - c.col()) < dmax)
+	    --p.col();
+
+	  internal::update_link_array(lbl, right_link, p, c, i, dmax);
+
+	}
+
+	trace::exiting("scribo::text::grouping::group_with_single_right_link");
+	return right_link;
+      }
+
+# endif // ! MLN_INCLUDE_ONLY
+
+    } // end of namespace scribo::text::grouping
+
+  } // end of namespace scribo::text
+
+} // end of namespace scribo
+
+#endif // ! SCRIBO_TEXT_GROUPING_GROUP_WITH_SINGLE_RIGHT_LINK_HH
diff --git a/milena/sandbox/scribo/photo.cc b/milena/sandbox/scribo/text/grouping/internal/find_root.hh
similarity index 52%
copy from milena/sandbox/scribo/photo.cc
copy to milena/sandbox/scribo/text/grouping/internal/find_root.hh
index a173750..0b1e307 100644
--- a/milena/sandbox/scribo/photo.cc
+++ b/milena/sandbox/scribo/text/grouping/internal/find_root.hh
@@ -1,4 +1,5 @@
-// Copyright (C) 2008 EPITA Research and Development Laboratory
+// 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
@@ -26,39 +27,54 @@
 // Public License.
 
 
-#include "demat.hh"
+#ifndef SCRIBO_TEXT_GROUPING_INTERNAL_FIND_ROOT_LINK_ARRAY_HH
+# define SCRIBO_TEXT_GROUPING_INTERNAL_FIND_ROOT_LINK_ARRAY_HH
 
-int main(int argc, char*argv[])
+/// \file scribo/text/grouping/internal/find_root.hh
+///
+/// Find root in a parent array arrays.
+
+
+# include <mln/util/array.hh>
+
+
+namespace scribo
 {
-  using namespace mln;
-  using value::int_u8;
 
-  if (argc < 2)
+  namespace text
   {
-    std::cout << argv[0] << " <in.pbm> <out.pgm> <l> <bbox_larger> <bbox_distance> <min_comp_nsites>" << std::endl
-	      << std::endl << std::endl
-	      << std::endl
-	      << "=========="
-	      << std::endl << std::endl
-	      << "<in.pbm>	    B/W inverted input image."
-	      << std::endl << std::endl
-/*	      << "<out.ppm>	    RGB8 output image."
-	      << std::endl << std::endl
-	      << "<bbox_distance>     Maximum distance between character bounding boxes. Used for bbox grouping."
-	      << std::endl << std::endl
-	      << "<min_comp_nsites>   Minimum site count of a character/text component."
-	      << std::endl
-	      << "		    If a component have a site count lesser than this value, it is erased."
-	      << std::endl << std::endl
-	      << std::endl*/
-	      << "=========="
-	      << std::endl << std::endl
-	      << "HINT: compile with -DNOUT to avoid debug images."
-	      << std::endl << std::endl;
-    return 1;
-  }
-
-  scribo::demat_photo(argv);
-
-  return 0;
-}
+
+    namespace grouping
+    {
+
+      namespace internal
+      {
+
+	/// Find root in a parent array arrays.
+	unsigned
+        find_root(util::array<unsigned>& parent, unsigned x);
+
+# ifndef MLN_INCLUDE_ONLY
+
+	inline
+	unsigned
+        find_root(util::array<unsigned>& parent, unsigned x)
+        {
+          if (parent[x] == x)
+	    return x;
+	  else
+	    return parent[x] = find_root(parent, parent[x]);
+	}
+
+# endif // ! MLN_INCLUDE_ONLY
+
+      } // end of namespace scribo::text::grouping::internal
+
+    } // end of namespace scribo::text::grouping
+
+  } // end of namespace scribo::text
+
+} // end of namespace scribo
+
+
+#endif // ! SCRIBO_TEXT_GROUPING_INTERNAL_FIND_ROOT_LINK_ARRAY_HH
diff --git a/milena/sandbox/scribo/photo.cc b/milena/sandbox/scribo/text/grouping/internal/init_link_array.hh
similarity index 52%
rename from milena/sandbox/scribo/photo.cc
rename to milena/sandbox/scribo/text/grouping/internal/init_link_array.hh
index a173750..ba197f9 100644
--- a/milena/sandbox/scribo/photo.cc
+++ b/milena/sandbox/scribo/text/grouping/internal/init_link_array.hh
@@ -1,4 +1,5 @@
-// Copyright (C) 2008 EPITA Research and Development Laboratory
+// 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
@@ -26,39 +27,51 @@
 // Public License.
 
 
-#include "demat.hh"
+#ifndef SCRIBO_TEXT_GROUPING_INTERNAL_INIT_LINK_ARRAY_HH
+# define SCRIBO_TEXT_GROUPING_INTERNAL_INIT_LINK_ARRAY_HH
 
-int main(int argc, char*argv[])
+/// \file scribo/text/grouping/internal/init_link_array.hh
+///
+/// Initialize a link array.
+
+
+# include <mln/util/array.hh>
+
+
+namespace scribo
 {
-  using namespace mln;
-  using value::int_u8;
 
-  if (argc < 2)
+  namespace text
   {
-    std::cout << argv[0] << " <in.pbm> <out.pgm> <l> <bbox_larger> <bbox_distance> <min_comp_nsites>" << std::endl
-	      << std::endl << std::endl
-	      << std::endl
-	      << "=========="
-	      << std::endl << std::endl
-	      << "<in.pbm>	    B/W inverted input image."
-	      << std::endl << std::endl
-/*	      << "<out.ppm>	    RGB8 output image."
-	      << std::endl << std::endl
-	      << "<bbox_distance>     Maximum distance between character bounding boxes. Used for bbox grouping."
-	      << std::endl << std::endl
-	      << "<min_comp_nsites>   Minimum site count of a character/text component."
-	      << std::endl
-	      << "		    If a component have a site count lesser than this value, it is erased."
-	      << std::endl << std::endl
-	      << std::endl*/
-	      << "=========="
-	      << std::endl << std::endl
-	      << "HINT: compile with -DNOUT to avoid debug images."
-	      << std::endl << std::endl;
-    return 1;
-  }
-
-  scribo::demat_photo(argv);
-
-  return 0;
-}
+
+    namespace grouping
+    {
+
+      namespace internal
+      {
+
+	/// Initialize a link array.
+	void init_link_array(util::array<unsigned>& link_array);
+
+# ifndef MLN_INCLUDE_ONLY
+
+        inline
+        void
+	init_link_array(util::array<unsigned>& link_array)
+	{
+	  for (unsigned i = 0; i < link_array.nelements(); ++i)
+	    link_array[i] = i;
+	}
+
+# endif // ! MLN_INCLUDE_ONLY
+
+      } // end of namespace scribo::text::grouping::internal
+
+    } // end of namespace scribo::text::grouping
+
+  } // end of namespace scribo::text
+
+} // end of namespace scribo
+
+
+#endif // ! SCRIBO_TEXT_GROUPING_INTERNAL_INIT_LINK_ARRAY_HH
diff --git a/milena/sandbox/scribo/text/grouping/internal/update_link_array.hh b/milena/sandbox/scribo/text/grouping/internal/update_link_array.hh
new file mode 100644
index 0000000..242d18f
--- /dev/null
+++ b/milena/sandbox/scribo/text/grouping/internal/update_link_array.hh
@@ -0,0 +1,93 @@
+// 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.
+
+
+#ifndef SCRIBO_TEXT_GROUPING_INTERNAL_UPDATE_LINK_ARRAY_HH
+# define SCRIBO_TEXT_GROUPING_INTERNAL_UPDATE_LINK_ARRAY_HH
+
+/// \file scribo/text/grouping/internal/update_link_array.hh
+///
+/// Update a lookup table if a neighbor is found on the right of
+/// the current bbox.
+
+
+# include <mln/core/concept/image.hh>
+# include <mln/util/array.hh>
+
+
+namespace scribo
+{
+
+  namespace text
+  {
+
+    namespace grouping
+    {
+
+      namespace internal
+      {
+
+	/// Update the lookup table \p link_array if a neighbor is found
+	/// on the right of the current bbox.
+	template <typename I>
+	void
+	update_link_array(const Image<I>& lbl, util::array<unsigned>& link_array,
+			  const mln_site(I)& p, const mln_site(I)& c,
+			  unsigned i, int dmax);
+
+# ifndef MLN_INCLUDE_ONLY
+
+	template <typename I>
+	inline
+	void
+	update_link_array(const Image<I>& lbl_, util::array<unsigned>& link_array,
+			  const mln_site(I)& p, const mln_site(I)& c,
+			  unsigned i, int dmax)
+	{
+	  const I& lbl = exact(lbl_);
+
+	  mlc_is_a(mln_value(I), mln::value::Symbolic)::check();
+	  mln_assertion(lbl.is_valid());
+
+	  if (lbl.domain().has(p) && lbl(p) != literal::zero && lbl(p) != i
+	      && (math::abs(p.col() - c.col())) < dmax && link_array[lbl(p)] == lbl(p))
+	    link_array[lbl(p)] = i;
+	}
+
+# endif // ! MLN_INCLUDE_ONLY
+
+      } // end of namespace scribo::text::grouping::internal
+
+    } // end of namespace scribo::text::grouping
+
+  } // end of namespace scribo::text
+
+} // end of namespace scribo
+
+
+#endif // ! SCRIBO_TEXT_GROUPING_INTERNAL_UPDATE_LINK_ARRAY_HH
diff --git a/milena/sandbox/scribo/text/grouping/internal/update_link_graph.hh b/milena/sandbox/scribo/text/grouping/internal/update_link_graph.hh
new file mode 100644
index 0000000..e31c5e8
--- /dev/null
+++ b/milena/sandbox/scribo/text/grouping/internal/update_link_graph.hh
@@ -0,0 +1,92 @@
+// 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.
+
+
+#ifndef SCRIBO_TEXT_GROUPING_INTERNAL_UPDATE_LINK_GRAPH_HH
+# define SCRIBO_TEXT_GROUPING_INTERNAL_UPDATE_LINK_GRAPH_HH
+
+/// \file scribo/text/grouping/internal/update_link_array.hh
+///
+/// Update graph edges if a valid neighbor is found.
+
+# include <mln/core/concept/image.hh>
+# include <mln/util/graph.hh>
+
+namespace scribo
+{
+
+  namespace text
+  {
+
+    namespace grouping
+    {
+
+      namespace internal
+      {
+
+	/// Update graph edges if a valid neighbor is found.
+	template <typename I>
+	void
+	update_link_graph(Image<I>& lbl_, util::graph& g,
+			  const mln_site(I)& p, const mln_site(I)& c,
+			  unsigned i, int dmax);
+
+
+# ifndef MLN_INCLUDE_ONLY
+
+
+	template <typename I>
+	inline
+	void
+	update_link_graph(Image<I>& lbl_, util::graph& g,
+			  const mln_site(I)& p, const mln_site(I)& c,
+			  unsigned i, int dmax)
+	{
+	  const I& lbl = exact(lbl_);
+
+	  mlc_is_a(mln_value(I), mln::value::Symbolic)::check();
+	  mln_precondition(exact(lbl).is_valid());
+
+	  if (lbl.domain().has(p) && lbl(p) != 0u && lbl(p) != i
+	      && (math::abs(p.col() - c.col())) < dmax)
+	    g.add_edge(lbl(p), i);
+	}
+
+
+# endif // ! MLN_INCLUDE_ONLY
+
+      } // end of namespace scribo::text::grouping::internal
+
+    } // end of namespace scribo::text::grouping
+
+  } // end of namespace scribo::text
+
+} // end of namespace scribo
+
+
+#endif // ! SCRIBO_TEXT_GROUPING_INTERNAL_UPDATE_LINK_GRAPH_HH
diff --git a/milena/sandbox/scribo/text/recognition.hh b/milena/sandbox/scribo/text/recognition.hh
new file mode 100644
index 0000000..98c3b67
--- /dev/null
+++ b/milena/sandbox/scribo/text/recognition.hh
@@ -0,0 +1,142 @@
+// 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.
+
+#ifndef SCRIBO_TEXT_RECOGNITION_HH
+# define SCRIBO_TEXT_RECOGNITION_HH
+
+/// \file scribo/text/recognition.hh
+///
+/// Passes the text bounding boxes to an OCR (Tesseract).
+
+# include <mln/core/concept/image.hh>
+# include <mln/core/concept/neighborhood.hh>
+# include <mln/core/site_set/box.hh>
+# include <mln/util/array.hh>
+# include <mln/labeling/blobs.hh>
+# include <mln/data/fill.hh>
+# include <mln/pw/all.hh>
+# include <mln/debug/put_words.hh>
+
+# include <scribo/core/macros.hh>
+
+# include <tesseract/baseapi.h>
+
+
+
+namespace scribo
+{
+
+  namespace text
+  {
+
+    /// Passes the text bboxes to Tesseract (OCR) and store the result in
+    /// an image of characters.
+    ///
+    /// \param[in] input_ image from where the text bboxes are extracted.
+    /// \param[in] nbh_ The neighborhood used to label \p input_.
+    /// \param[in] nbboxes The value type used in the labeled image.
+    /// \param[in] textbboxes array of text bounding boxes.
+    /// \param[in] language the language which should be recognized by Tesseract.
+    ///		   (fra, en, ...)
+    ///
+    /// \return An image of characters.
+    ///
+    /// FIXME: For each text bbox, we create a new image. We may like to avoid that.
+    /// FIXME: Do not store the result in an image?
+    template <typename I, typename N, typename V>
+    mln_ch_value(I,char)
+    text_recognition(const Image<I>& input_,
+		     const Neighborhood<N>& nbh_, V& nbboxes,
+		     const util::array< box<mln_site(I)> >& textbboxes,
+		     const char *language);
+
+
+# ifndef MLN_INCLUDE_ONLY
+
+    template <typename I, typename N, typename V>
+    mln_ch_value(I,char)
+    text_recognition(const Image<I>& input_,
+		     const Neighborhood<N>& nbh_, V& nbboxes,
+		     const util::array< box<mln_site(I)> >& textbboxes,
+		     const char *language)
+    {
+      trace::entering("scribo::text::recognition");
+
+      mlc_equal(mln_value(I), bool)::check();
+      const I& input = exact(input_);
+      const N& nbh = exact(nbh_);
+      mln_precondition(input.is_valid());
+      mln_precondition(nbh.is_valid());
+
+      mln_ch_value(I,V) lbl = labeling::blobs(input, nbh, nbboxes);
+
+      /// Use text bboxes with Tesseract
+      TessBaseAPI::InitWithLanguage(NULL, NULL, language, NULL, false, 0, NULL);
+      mln_ch_value(I,char) txt(input.domain());
+      data::fill(txt, ' ');
+
+      for_all_components(i, textbboxes)
+      {
+	if (textbboxes[i].is_valid())
+	{
+	  mln_ch_value(I,bool) b(textbboxes[i], 0);
+	  data::fill(b, false);
+	  data::fill((b | (pw::value(lbl) == pw::cst(i))).rw(), true);
+
+	  char* s = TessBaseAPI::TesseractRect(
+	      (unsigned char*) b.buffer(),
+	      sizeof (bool),		  // Pixel size.
+	      b.ncols() * sizeof (bool),  // Row_offset
+	      0,			  // Left
+	      0,			  // Top
+	      b.ncols(),		  // n cols
+	      b.nrows());		  // n rows
+
+
+
+	  mln_site(I) p = textbboxes[i].center();
+	  p.col() -= (textbboxes[i].pmax().col()
+			  - textbboxes[i].pmin().col()) / 2;
+	  if (s != 0)
+	    debug::put_word(txt, p, s);
+	  free(s);
+	}
+      }
+
+      trace::exiting("scribo::text::recognition");
+      return text;
+    }
+
+
+# endif // ! MLN_INCLUDE_ONLY
+
+  } // end of namespace scribo::text
+
+} // end of namespace scribo
+
+#endif // ! SCRIBO_TEXT_RECOGNITION_HH
-- 
1.5.6.5
                    
                  
                  
                          
                            
                            1
                            
                          
                          
                            
                            0
                            
                          
                          
                            
    
                          
                        
                    
                    
                          URL: https://svn.lrde.epita.fr/svn/oln/trunk/milena/sandbox
ChangeLog:
2009-03-10  Fabien Freling  <fabien.freling(a)lrde.epita.fr>
	Implement 3D watershed and various tools.
	* fabien/bin/dicom2pgm.cc: New tool.
	* fabien/bin/dump2pgm.cc: New tool.
	* fabien/bin/dump2pgm8b.cc: New tool.
	* fabien/bin/dump_mask.cc: New tool.
	* fabien/bin/filetype.hh: New.
	* fabien/bin/pnms2dump.cc: New tool.
	* fabien/igr/Makefile: Update.
	* fabien/igr/check2d_wsd.sh: Shell script for testing.
	* fabien/igr/check3d_wsd.sh: Shell script for testing.
	* fabien/igr/watershed.cc: Rename this...
	* fabien/igr/watershed.hh: ...into this.
	* fabien/igr/watershed2d.cc: Implement 2D watershed.
	* fabien/igr/watershed3d.cc: Implement 3D watershed.
---
 TODO               |    7 -
 bin/dicom2pgm.cc   |   34 +++++
 bin/dump2pgm.cc    |   37 +++++
 bin/dump2pgm8b.cc  |   38 ++++++
 bin/dump_mask.cc   |   86 +++++++++++++
 bin/filetype.hh    |   54 ++++++++
 bin/pnms2dump.cc   |   97 +++++++++++++++
 igr/Makefile       |    9 -
 igr/check2d_wsd.sh |   32 +++++
 igr/check3d_wsd.sh |   34 +++++
 igr/watershed.hh   |  331 ++++++++++++++++++++++++++++++++++++++++++++++++++++
 igr/watershed2d.cc |  331 ++++++++++++++++++++++++++++++++++++++++++++++++++++
 igr/watershed3d.cc |  334 +++++++++++++++++++++++++++++++++++++++++++++++++++++
 13 files changed, 1419 insertions(+), 5 deletions(-)
Index: trunk/milena/sandbox/fabien/igr/watershed.cc (deleted)
===================================================================
Index: trunk/milena/sandbox/fabien/igr/watershed2d.cc
===================================================================
--- trunk/milena/sandbox/fabien/igr/watershed2d.cc	(revision 0)
+++ trunk/milena/sandbox/fabien/igr/watershed2d.cc	(revision 3503)
@@ -0,0 +1,331 @@
+#include <iostream>
+#include <mln/core/image/image2d.hh>
+
+#include <mln/core/alias/neighb2d.hh>
+#include <mln/core/alias/window2d.hh>
+#include <mln/core/image/image_if.hh>
+
+#include <mln/io/ppm/save.hh>
+#include <mln/io/ppm/load.hh>
+#include <mln/io/pgm/load.hh>
+#include <mln/io/dicom/load.hh>
+#include <mln/io/pgm/save.hh>
+#include <mln/io/dump/save.hh>
+
+#include <mln/value/rgb8.hh>
+#include <mln/value/int_u8.hh>
+#include <mln/value/int_u12.hh>
+#include <mln/value/label_16.hh>
+
+#include <mln/level/transform.hh>
+#include <mln/level/stretch.hh>
+
+#include <mln/labeling/mean_values.hh>
+
+#include <mln/convert/to_fun.hh>
+
+#include <mln/make/graph.hh>
+
+#include <mln/morpho/gradient.hh>
+#include <mln/morpho/closing/area.hh>
+#include <mln/morpho/meyer_wst.hh>
+
+#include <mln/fun/l2l/wrap.hh>
+
+#include <mln/core/var.hh>
+#include <mln/morpho/elementary/dilation.hh>
+
+#include <mln/core/routine/extend.hh>
+
+#include <mln/util/graph.hh>
+
+#include <mln/essential/2d.hh>
+#include <mln/core/alias/vec3d.hh>
+#include <mln/debug/draw_graph.hh>
+#include <mln/util/graph.hh>
+#include <mln/accu/center.hh>
+#include <mln/io/dump/all.hh>
+#include <mln/value/label_8.hh>
+#include <mln/value/rgb16.hh>
+#include <mln/accu/compute.hh>
+#include <mln/core/alias/dpoint2d.hh>
+#include <mln/draw/box.hh>
+#include <mln/level/stretch.hh>
+#include <mln/fun/v2v/id.hh>
+#include <mln/fun/l2l/wrap.hh>
+#include <mln/core/image/line_graph_elt_neighborhood.hh>
+#include <mln/morpho/elementary/dilation.hh>
+#include <mln/labeling/mean_values.hh>
+#include <mln/extension/adjust_fill.hh>
+#include <mln/extract/all.hh>
+#include <mln/make/region_adjacency_graph.hh>
+
+
+
+// Given a color image and a wshed image, computes the component graph.
+// Vertex values are computed thanks to a RGB image.
+
+namespace mln
+{
+
+  template <typename V>
+  value::int_u8 dist(const V& c1, const V& c2)
+  {
+    unsigned d = math::diff_abs(c1.red(), c2.red());
+    unsigned d_;
+    d_ = math::diff_abs(c1.green(), c2.green());
+
+    if (d_ > d)
+      d = d_;
+
+    d_ = math::diff_abs(c1.blue(), c2.blue());
+
+    if (d_ > d)
+      d = d_;
+    return d;
+  }
+
+
+  // ima_v, image on graph vertices; value = mean color per vertex (watershed basin)
+  template <typename I>
+  inline
+  pw::image<fun::i2v::array<value::int_u8>,
+	    p_edges<util::graph, fun::i2v::array<mln_site(I)> > >
+  make_edge_graph_image(const I& ima_v, const util::graph& g)
+  {
+    // edge sites.
+    typedef fun::i2v::array<mln_site(I)> edge_site_t;
+    edge_site_t edge_site(g.e_nmax(), literal::origin);
+    typedef p_edges<util::graph, edge_site_t > pe_t;
+    pe_t pe(g, edge_site);
+
+    // edge values
+    typedef fun::i2v::array<value::int_u8> edge_values_t;
+    edge_values_t edge_values(g.e_nmax());
+
+    // image on graph edges
+    typedef pw::image<edge_values_t, pe_t> ima_e_t;
+    ima_e_t ima_e = (edge_values | pe);
+
+    mln_piter(ima_e_t) e(ima_e.domain());
+    for_all(e) // in ima_e
+      ima_e(e) = math::diff_abs(ima_v.function()(e.element().v1()), ima_v.function()(e.element().v2()));
+
+    return ima_e;
+  }
+
+
+  template <typename I, typename J>
+  inline
+  util::array<mln_value(I)>
+  mean_color_values(const I& input, const J& w, mln_value(J) nbasins)
+  {
+    // Cf. sandbox/theo/color/segment_rgb_pixels.cc
+
+    util::array<float> m_3f = labeling::compute(accu::mean<mln_value(I)>(),
+						input, // input color image
+						w, // watershed labeling
+						nbasins);
+    m_3f[0] = literal::zero;
+
+    util::array<mln_value(I)> m;
+    convert::from_to(m_3f, m);
+    m[0] = 150u;
+
+    /*io::ppm::save(level::transform(w,
+	  convert::to< fun::i2v::array<mln_value(I)> >(m)),
+	"wst_rag_wshd_color.ppm");*/
+
+    return m;
+  }
+
+
+  template <typename I, typename J>
+  pw::image<fun::i2v::array<mln_value(I)>, p_vertices<util::graph, fun::i2v::array<mln_site(I)> > >
+  make_vertex_graph_image(const util::graph& g, const I&input, const J& w, const mln_value(J)& nbasins)
+  {
+    typedef util::array<mln_site(I)> vertex_sites_t;
+    vertex_sites_t site_values;
+    convert::from_to(labeling::compute(accu::center<mln_site(I)>(), w, nbasins),
+				       site_values);
+
+    typedef fun::i2v::array<mln_site(J)> f_sites_t;
+    f_sites_t sites;
+    convert::from_to(site_values, sites);
+
+    // p_vertices
+    typedef p_vertices<util::graph, f_sites_t> S;
+    S pv(g, sites);
+
+
+    typedef fun::i2v::array<mln_value(I)> vertex_values_t;
+    vertex_values_t vertex_values;
+    convert::from_to(mean_color_values(input, w, nbasins), vertex_values);
+
+    mln_VAR(ima_v, (vertex_values | pv));
+
+    return ima_v;
+  }
+
+
+  template <typename I, typename V>
+  struct edge_to_color : Function_p2v< edge_to_color<I,V> >
+  {
+    typedef V result;
+
+    edge_to_color(const I& ima)
+      : ima_(ima)
+    {
+    }
+
+    V
+    operator()(const unsigned& e) const
+    {
+      return convert::to<V>(ima_.function()(e));
+    }
+
+    const I& ima_;
+  };
+
+  template <typename V>
+  inline
+  unsigned
+  find_root(util::array<V>& parent, const unsigned& x)
+  {
+    if (parent[x] == x)
+      return x;
+    else
+      return parent[x] = find_root(parent, parent[x]);
+  }
+
+
+  template <typename I, typename V, typename E>
+  inline
+  image2d<mln_value(I)>
+  make_debug_graph_image(const I& input,
+			 const V& ima_v, const E& ima_e,
+			 unsigned box_size, const value::int_u12& bg)
+  {
+    image2d<mln_value(I)> ima;
+    initialize(ima, input);
+
+    data::fill(ima, bg);
+    debug::draw_graph(ima, ima_v.domain(),
+		      pw::cst(150u),
+		      edge_to_color<E, mln_value(I)>(ima_e));
+
+    dpoint2d tl(-box_size,-box_size);
+    dpoint2d br(box_size,box_size);
+    mln_piter(V) p(ima_v.domain());
+    for_all(p)
+    {
+      box2d b(p + tl, p + br);
+      b.crop_wrt(ima.domain());
+      data::fill((ima | b).rw(), convert::to<mln_value(I)>(ima_v(p)));
+    }
+
+    return ima;
+  }
+
+}
+
+
+///////////////////
+//               //
+// Main Function //
+//               //
+///////////////////
+
+
+int main(int argc, char *argv[])
+{
+  using namespace mln;
+  using value::int_u8;
+  using value::int_u12;
+  using value::rgb8;
+  using value::label_16;
+
+  if (argc < 4)
+  {
+    std::cout << "Usage: " << argv[0] << " <ima.dcm> <closure_lambda> <box_size> <dist_max>"
+	      << std::endl;
+    return 1;
+  }
+
+  unsigned closure_lambda = atoi(argv[2]);
+  unsigned box_size = atoi(argv[3]);
+  unsigned dist_max = atoi(argv[4]);
+
+  image2d<int_u12> dcm;
+  io::dicom::load(dcm, argv[1]);
+
+  io::pgm::save(level::stretch(int_u8(), dcm), "wsd_01_src.pgm");
+
+  image2d<int_u12> grad = morpho::gradient(dcm, win_c4p());
+  image2d<int_u12> clo = morpho::closing::area(grad, c4(), closure_lambda);
+
+  label_16 nbasins;
+  image2d<label_16> wshed = morpho::meyer_wst(clo, c4(), nbasins);
+
+  io::pgm::save(level::stretch(int_u8(), clo), "wsd_02.pgm");
+  io::pgm::save(level::transform(wshed, fun::l2l::wrap<int_u8>()), "wsd_03.pgm");
+
+  mln_VAR(vol2_, morpho::elementary::dilation(extend(wshed | (pw::value(wshed) == 0u), wshed), c8()));
+
+  data::fill((wshed | (pw::value(wshed) == 0u)).rw(), vol2_);
+
+  io::pgm::save(level::transform(wshed, fun::l2l::wrap<int_u8>()), "wsd_04.pgm");
+
+  /// Build graph
+  util::graph g = make::graph(wshed, c4(), nbasins);
+  // Build graph images and compute distance values with a RGB image.
+  mln_VAR(ima_v, make_vertex_graph_image(g, dcm, wshed, nbasins));
+  mln_VAR(ima_e, make_edge_graph_image(ima_v, g));
+
+  /// Try to merge vertices.
+  mln_piter_(ima_e_t) e(ima_e.domain());
+  util::array<label_16> parent(g.v_nmax());
+  for (unsigned i = 0; i < parent.nelements(); ++i)
+    parent[i] = i;
+
+  for_all(e)
+  {
+    unsigned v1 = e.element().v1();
+    unsigned v2 = e.element().v2();
+    if (ima_e(e) <= dist_max && find_root(parent, v1) != find_root(parent, v2))
+      parent[find_root(parent, v1)] = find_root(parent, v2);
+  }
+
+  fun::i2v::array<label_16> f(parent.nelements());
+  std::vector<unsigned> new_label(parent.nelements(), 0);
+  unsigned nbasins2 = 0;
+  for (unsigned i = 0; i < parent.nelements(); ++i)
+  {
+    unsigned p = find_root(parent, i);
+    mln_assertion(parent[p] == find_root(parent, i));
+    if (new_label[p] == 0)
+      new_label[p] = nbasins2++;
+    f(i) = new_label[p];
+  }
+  mln_invariant(f(0) == 0u);
+  --nbasins2; // nbasins2 does not count the basin with label 0.
+  image2d<label_16> wsd2 = level::transform(wshed, f);
+
+  io::pgm::save(level::transform(wsd2, fun::l2l::wrap<int_u8>()), "wsd_05.pgm");
+
+  /// Reconstruct a graph from the simplified image.
+  util::graph g2 = make::graph(wsd2, c4(), nbasins2);
+
+  // Compute distance values with a RGB image.
+  mln_VAR(ima_v2, make_vertex_graph_image(g2, dcm, wsd2, nbasins2));
+  mln_VAR(ima_e2, make_edge_graph_image(ima_v2, g2));
+
+  mln_VAR(wsd2_, morpho::elementary::dilation(extend(wsd2 | (pw::value(wsd2) == 0u), wsd2), c8()));
+
+  data::fill((wsd2 | (pw::value(wsd2) == 0u)).rw(), wsd2_);
+
+  io::pgm::save(level::transform(labeling::mean_values(dcm, wsd2, nbasins2), fun::l2l::wrap<int_u8>()), "wsd_06_mean_colors.pgm");
+  io::pgm::save(level::stretch(int_u8(), make_debug_graph_image(dcm, ima_v2, ima_e2, box_size, 4095)), "wsd_07_graph_image2_white.pgm");
+  io::pgm::save(level::stretch(int_u8(), make_debug_graph_image(dcm, ima_v2, ima_e2, box_size, 0)), "wsd_08_graph_image2_black.pgm");
+  io::pgm::save(level::transform(wsd2, fun::l2l::wrap<int_u8>()), "wsd_99_result.pgm");
+}
Index: trunk/milena/sandbox/fabien/igr/watershed3d.cc
===================================================================
--- trunk/milena/sandbox/fabien/igr/watershed3d.cc	(revision 0)
+++ trunk/milena/sandbox/fabien/igr/watershed3d.cc	(revision 3503)
@@ -0,0 +1,334 @@
+#include <iostream>
+#include <mln/core/image/image2d.hh>
+#include <mln/core/image/image3d.hh>
+
+#include <mln/core/alias/neighb2d.hh>
+#include <mln/core/alias/window2d.hh>
+#include <mln/core/alias/neighb3d.hh>
+#include <mln/core/alias/window3d.hh>
+#include <mln/core/image/image_if.hh>
+
+#include <mln/io/ppm/save.hh>
+#include <mln/io/ppm/load.hh>
+#include <mln/io/pgm/load.hh>
+#include <mln/io/dicom/load.hh>
+#include <mln/io/pgm/save.hh>
+#include <mln/io/dump/save.hh>
+
+#include <mln/value/rgb8.hh>
+#include <mln/value/int_u8.hh>
+#include <mln/value/int_u12.hh>
+#include <mln/value/label_16.hh>
+
+#include <mln/level/transform.hh>
+#include <mln/level/stretch.hh>
+
+#include <mln/labeling/mean_values.hh>
+
+#include <mln/convert/to_fun.hh>
+
+#include <mln/make/graph.hh>
+
+#include <mln/morpho/gradient.hh>
+#include <mln/morpho/closing/area.hh>
+#include <mln/morpho/meyer_wst.hh>
+
+#include <mln/fun/l2l/wrap.hh>
+
+#include <mln/core/var.hh>
+#include <mln/morpho/elementary/dilation.hh>
+
+#include <mln/core/routine/extend.hh>
+
+#include <mln/util/graph.hh>
+
+#include <mln/essential/2d.hh>
+#include <mln/core/alias/vec3d.hh>
+#include <mln/debug/draw_graph.hh>
+#include <mln/util/graph.hh>
+#include <mln/accu/center.hh>
+#include <mln/io/dump/all.hh>
+#include <mln/value/label_8.hh>
+#include <mln/value/rgb16.hh>
+#include <mln/accu/compute.hh>
+#include <mln/core/alias/dpoint2d.hh>
+#include <mln/draw/box.hh>
+#include <mln/level/stretch.hh>
+#include <mln/fun/v2v/id.hh>
+#include <mln/fun/l2l/wrap.hh>
+#include <mln/core/image/line_graph_elt_neighborhood.hh>
+#include <mln/morpho/elementary/dilation.hh>
+#include <mln/labeling/mean_values.hh>
+#include <mln/extension/adjust_fill.hh>
+#include <mln/extract/all.hh>
+#include <mln/make/region_adjacency_graph.hh>
+
+
+
+// Given a color image and a wshed image, computes the component graph.
+// Vertex values are computed thanks to a RGB image.
+
+namespace mln
+{
+
+  template <typename V>
+  value::int_u8 dist(const V& c1, const V& c2)
+  {
+    unsigned d = math::diff_abs(c1.red(), c2.red());
+    unsigned d_;
+    d_ = math::diff_abs(c1.green(), c2.green());
+
+    if (d_ > d)
+      d = d_;
+
+    d_ = math::diff_abs(c1.blue(), c2.blue());
+
+    if (d_ > d)
+      d = d_;
+    return d;
+  }
+
+
+  // ima_v, image on graph vertices; value = mean color per vertex (watershed basin)
+  template <typename I>
+  inline
+  pw::image<fun::i2v::array<value::int_u8>,
+	    p_edges<util::graph, fun::i2v::array<mln_site(I)> > >
+  make_edge_graph_image(const I& ima_v, const util::graph& g)
+  {
+    // edge sites.
+    typedef fun::i2v::array<mln_site(I)> edge_site_t;
+    edge_site_t edge_site(g.e_nmax(), literal::origin);
+    typedef p_edges<util::graph, edge_site_t > pe_t;
+    pe_t pe(g, edge_site);
+
+    // edge values
+    typedef fun::i2v::array<value::int_u8> edge_values_t;
+    edge_values_t edge_values(g.e_nmax());
+
+    // image on graph edges
+    typedef pw::image<edge_values_t, pe_t> ima_e_t;
+    ima_e_t ima_e = (edge_values | pe);
+
+    mln_piter(ima_e_t) e(ima_e.domain());
+    for_all(e) // in ima_e
+      ima_e(e) = math::diff_abs(ima_v.function()(e.element().v1()), ima_v.function()(e.element().v2()));
+
+    return ima_e;
+  }
+
+
+  template <typename I, typename J>
+  inline
+  util::array<mln_value(I)>
+  mean_color_values(const I& input, const J& w, mln_value(J) nbasins)
+  {
+    // Cf. sandbox/theo/color/segment_rgb_pixels.cc
+
+    util::array<float> m_3f = labeling::compute(accu::mean<mln_value(I)>(),
+						input, // input color image
+						w, // watershed labeling
+						nbasins);
+    m_3f[0] = literal::zero;
+
+    util::array<mln_value(I)> m;
+    convert::from_to(m_3f, m);
+    m[0] = 150u;
+
+    /*io::ppm::save(level::transform(w,
+	  convert::to< fun::i2v::array<mln_value(I)> >(m)),
+	"wst_rag_wshd_color.ppm");*/
+
+    return m;
+  }
+
+
+  template <typename I, typename J>
+  pw::image<fun::i2v::array<mln_value(I)>, p_vertices<util::graph, fun::i2v::array<mln_site(I)> > >
+  make_vertex_graph_image(const util::graph& g, const I&input, const J& w, const mln_value(J)& nbasins)
+  {
+    typedef util::array<mln_site(I)> vertex_sites_t;
+    vertex_sites_t site_values;
+    convert::from_to(labeling::compute(accu::center<mln_site(I)>(), w, nbasins),
+				       site_values);
+
+    typedef fun::i2v::array<mln_site(J)> f_sites_t;
+    f_sites_t sites;
+    convert::from_to(site_values, sites);
+
+    // p_vertices
+    typedef p_vertices<util::graph, f_sites_t> S;
+    S pv(g, sites);
+
+
+    typedef fun::i2v::array<mln_value(I)> vertex_values_t;
+    vertex_values_t vertex_values;
+    convert::from_to(mean_color_values(input, w, nbasins), vertex_values);
+
+    mln_VAR(ima_v, (vertex_values | pv));
+
+    return ima_v;
+  }
+
+
+  template <typename I, typename V>
+  struct edge_to_color : Function_p2v< edge_to_color<I,V> >
+  {
+    typedef V result;
+
+    edge_to_color(const I& ima)
+      : ima_(ima)
+    {
+    }
+
+    V
+    operator()(const unsigned& e) const
+    {
+      return convert::to<V>(ima_.function()(e));
+    }
+
+    const I& ima_;
+  };
+
+  template <typename V>
+  inline
+  unsigned
+  find_root(util::array<V>& parent, const unsigned& x)
+  {
+    if (parent[x] == x)
+      return x;
+    else
+      return parent[x] = find_root(parent, parent[x]);
+  }
+
+
+  template <typename I, typename V, typename E>
+  inline
+  image3d<mln_value(I)>
+  make_debug_graph_image(const I& input,
+			 const V& ima_v, const E& ima_e,
+			 unsigned box_size, const value::int_u12& bg)
+  {
+    image3d<mln_value(I)> ima;
+    initialize(ima, input);
+
+    data::fill(ima, bg);
+    debug::draw_graph(ima, ima_v.domain(),
+		      pw::cst(150u),
+		      edge_to_color<E, mln_value(I)>(ima_e));
+
+    dpoint3d tl(-box_size,-box_size, -box_size);
+    dpoint3d br(box_size,box_size, -box_size);
+    mln_piter(V) p(ima_v.domain());
+    for_all(p)
+    {
+      box3d b(p + tl, p + br);
+      b.crop_wrt(ima.domain());
+      data::fill((ima | b).rw(), convert::to<mln_value(I)>(ima_v(p)));
+    }
+
+    return ima;
+  }
+
+}
+
+
+///////////////////
+//               //
+// Main Function //
+//               //
+///////////////////
+
+
+int main(int argc, char *argv[])
+{
+  using namespace mln;
+  using value::int_u8;
+  using value::int_u12;
+  using value::rgb8;
+  using value::label_16;
+
+  if (argc < 4)
+  {
+    std::cout << "Usage: " << argv[0] << " <ima.dcm> <closure_lambda> <box_size> <dist_max>"
+	      << std::endl;
+    return 1;
+  }
+
+  unsigned closure_lambda = atoi(argv[2]);
+  unsigned box_size = atoi(argv[3]);
+  unsigned dist_max = atoi(argv[4]);
+
+  image3d<int_u12> dcm;
+  io::dicom::load(dcm, argv[1]);
+
+  io::dump::save(level::stretch(int_u8(), dcm), "wsd_01_src.dump");
+
+  image3d<int_u12> grad = morpho::gradient(dcm, win_c4p_3d());
+  image3d<int_u12> clo = morpho::closing::area(grad, c6(), closure_lambda);
+
+  label_16 nbasins;
+  image3d<label_16> wshed = morpho::meyer_wst(clo, c6(), nbasins);
+
+  io::dump::save(level::stretch(int_u8(), clo), "wsd_02.dump");
+  io::dump::save(level::transform(wshed, fun::l2l::wrap<int_u8>()), "wsd_03.dump");
+
+  mln_VAR(vol2_, morpho::elementary::dilation(extend(wshed | (pw::value(wshed) == 0u), wshed), c26()));
+
+  data::fill((wshed | (pw::value(wshed) == 0u)).rw(), vol2_);
+
+  io::dump::save(level::transform(wshed, fun::l2l::wrap<int_u8>()), "wsd_04.dump");
+
+  /// Build graph
+  util::graph g = make::graph(wshed, c6(), nbasins);
+  // Build graph images and compute distance values with a RGB image.
+  mln_VAR(ima_v, make_vertex_graph_image(g, dcm, wshed, nbasins));
+  mln_VAR(ima_e, make_edge_graph_image(ima_v, g));
+
+  /// Try to merge vertices.
+  mln_piter_(ima_e_t) e(ima_e.domain());
+  util::array<label_16> parent(g.v_nmax());
+  for (unsigned i = 0; i < parent.nelements(); ++i)
+    parent[i] = i;
+
+  for_all(e)
+  {
+    unsigned v1 = e.element().v1();
+    unsigned v2 = e.element().v2();
+    if (ima_e(e) <= dist_max && find_root(parent, v1) != find_root(parent, v2))
+      parent[find_root(parent, v1)] = find_root(parent, v2);
+  }
+
+  fun::i2v::array<label_16> f(parent.nelements());
+  std::vector<unsigned> new_label(parent.nelements(), 0);
+  unsigned nbasins2 = 0;
+  for (unsigned i = 0; i < parent.nelements(); ++i)
+  {
+    unsigned p = find_root(parent, i);
+    mln_assertion(parent[p] == find_root(parent, i));
+    if (new_label[p] == 0)
+      new_label[p] = nbasins2++;
+    f(i) = new_label[p];
+  }
+  mln_invariant(f(0) == 0u);
+  --nbasins2; // nbasins2 does not count the basin with label 0.
+  image3d<label_16> wsd2 = level::transform(wshed, f);
+
+  io::dump::save(level::transform(wsd2, fun::l2l::wrap<int_u8>()), "wsd_05.dump");
+
+  /// Reconstruct a graph from the simplified image.
+  util::graph g2 = make::graph(wsd2, c6(), nbasins2);
+
+  // Compute distance values with a RGB image.
+  mln_VAR(ima_v2, make_vertex_graph_image(g2, dcm, wsd2, nbasins2));
+  mln_VAR(ima_e2, make_edge_graph_image(ima_v2, g2));
+
+  mln_VAR(wsd2_, morpho::elementary::dilation(extend(wsd2 | (pw::value(wsd2) == 0u), wsd2), c26()));
+
+  data::fill((wsd2 | (pw::value(wsd2) == 0u)).rw(), wsd2_);
+
+  io::dump::save(level::transform(labeling::mean_values(dcm, wsd2, nbasins2), fun::l2l::wrap<int_u8>()), "wsd_06_mean_colors.dump");
+  //io::dump::save(level::stretch(int_u8(), make_debug_graph_image(dcm, ima_v2, ima_e2, box_size, 4095)), "wsd_07_graph_image2_white.dump");
+  //io::dump::save(level::stretch(int_u8(), make_debug_graph_image(dcm, ima_v2, ima_e2, box_size, 0)), "wsd_08_graph_image2_black.dump");
+  io::dump::save(level::transform(wsd2, fun::l2l::wrap<int_u8>()), "wsd_99_result.dump");
+}
Index: trunk/milena/sandbox/fabien/igr/watershed.hh
===================================================================
--- trunk/milena/sandbox/fabien/igr/watershed.hh	(revision 0)
+++ trunk/milena/sandbox/fabien/igr/watershed.hh	(revision 3503)
@@ -0,0 +1,331 @@
+#include <iostream>
+#include <mln/core/image/image2d.hh>
+
+#include <mln/core/alias/neighb2d.hh>
+#include <mln/core/alias/window2d.hh>
+#include <mln/core/image/image_if.hh>
+
+#include <mln/io/ppm/save.hh>
+#include <mln/io/ppm/load.hh>
+#include <mln/io/pgm/load.hh>
+#include <mln/io/dicom/load.hh>
+#include <mln/io/pgm/save.hh>
+#include <mln/io/dump/save.hh>
+
+#include <mln/value/rgb8.hh>
+#include <mln/value/int_u8.hh>
+#include <mln/value/int_u12.hh>
+#include <mln/value/label_16.hh>
+
+#include <mln/level/transform.hh>
+#include <mln/level/stretch.hh>
+
+#include <mln/labeling/mean_values.hh>
+
+#include <mln/convert/to_fun.hh>
+
+#include <mln/make/graph.hh>
+
+#include <mln/morpho/gradient.hh>
+#include <mln/morpho/closing/area.hh>
+#include <mln/morpho/meyer_wst.hh>
+
+#include <mln/fun/l2l/wrap.hh>
+
+#include <mln/core/var.hh>
+#include <mln/morpho/elementary/dilation.hh>
+
+#include <mln/core/routine/extend.hh>
+
+#include <mln/util/graph.hh>
+
+#include <mln/essential/2d.hh>
+#include <mln/core/alias/vec3d.hh>
+#include <mln/debug/draw_graph.hh>
+#include <mln/util/graph.hh>
+#include <mln/accu/center.hh>
+#include <mln/io/dump/all.hh>
+#include <mln/value/label_8.hh>
+#include <mln/value/rgb16.hh>
+#include <mln/accu/compute.hh>
+#include <mln/core/alias/dpoint2d.hh>
+#include <mln/draw/box.hh>
+#include <mln/level/stretch.hh>
+#include <mln/fun/v2v/id.hh>
+#include <mln/fun/l2l/wrap.hh>
+#include <mln/core/image/line_graph_elt_neighborhood.hh>
+#include <mln/morpho/elementary/dilation.hh>
+#include <mln/labeling/mean_values.hh>
+#include <mln/extension/adjust_fill.hh>
+#include <mln/extract/all.hh>
+#include <mln/make/region_adjacency_graph.hh>
+
+
+
+// Given a color image and a wshed image, computes the component graph.
+// Vertex values are computed thanks to a RGB image.
+
+namespace mln
+{
+
+  template <typename V>
+  value::int_u8 dist(const V& c1, const V& c2)
+  {
+    unsigned d = math::diff_abs(c1.red(), c2.red());
+    unsigned d_;
+    d_ = math::diff_abs(c1.green(), c2.green());
+
+    if (d_ > d)
+      d = d_;
+
+    d_ = math::diff_abs(c1.blue(), c2.blue());
+
+    if (d_ > d)
+      d = d_;
+    return d;
+  }
+
+
+  // ima_v, image on graph vertices; value = mean color per vertex (watershed basin)
+  template <typename I>
+  inline
+  pw::image<fun::i2v::array<value::int_u8>,
+	    p_edges<util::graph, fun::i2v::array<mln_site(I)> > >
+  make_edge_graph_image(const I& ima_v, const util::graph& g)
+  {
+    // edge sites.
+    typedef fun::i2v::array<mln_site(I)> edge_site_t;
+    edge_site_t edge_site(g.e_nmax(), literal::origin);
+    typedef p_edges<util::graph, edge_site_t > pe_t;
+    pe_t pe(g, edge_site);
+
+    // edge values
+    typedef fun::i2v::array<value::int_u8> edge_values_t;
+    edge_values_t edge_values(g.e_nmax());
+
+    // image on graph edges
+    typedef pw::image<edge_values_t, pe_t> ima_e_t;
+    ima_e_t ima_e = (edge_values | pe);
+
+    mln_piter(ima_e_t) e(ima_e.domain());
+    for_all(e) // in ima_e
+      ima_e(e) = math::diff_abs(ima_v.function()(e.element().v1()), ima_v.function()(e.element().v2()));
+
+    return ima_e;
+  }
+
+
+  template <typename I, typename J>
+  inline
+  util::array<mln_value(I)>
+  mean_color_values(const I& input, const J& w, mln_value(J) nbasins)
+  {
+    // Cf. sandbox/theo/color/segment_rgb_pixels.cc
+
+    util::array<float> m_3f = labeling::compute(accu::mean<mln_value(I)>(),
+						input, // input color image
+						w, // watershed labeling
+						nbasins);
+    m_3f[0] = literal::zero;
+
+    util::array<mln_value(I)> m;
+    convert::from_to(m_3f, m);
+    m[0] = 150u;
+
+    /*io::ppm::save(level::transform(w,
+	  convert::to< fun::i2v::array<mln_value(I)> >(m)),
+	"wst_rag_wshd_color.ppm");*/
+
+    return m;
+  }
+
+
+  template <typename I, typename J>
+  pw::image<fun::i2v::array<mln_value(I)>, p_vertices<util::graph, fun::i2v::array<mln_site(I)> > >
+  make_vertex_graph_image(const util::graph& g, const I&input, const J& w, const mln_value(J)& nbasins)
+  {
+    typedef util::array<mln_site(I)> vertex_sites_t;
+    vertex_sites_t site_values;
+    convert::from_to(labeling::compute(accu::center<mln_site(I)>(), w, nbasins),
+				       site_values);
+
+    typedef fun::i2v::array<mln_site(J)> f_sites_t;
+    f_sites_t sites;
+    convert::from_to(site_values, sites);
+
+    // p_vertices
+    typedef p_vertices<util::graph, f_sites_t> S;
+    S pv(g, sites);
+
+
+    typedef fun::i2v::array<mln_value(I)> vertex_values_t;
+    vertex_values_t vertex_values;
+    convert::from_to(mean_color_values(input, w, nbasins), vertex_values);
+
+    mln_VAR(ima_v, (vertex_values | pv));
+
+    return ima_v;
+  }
+
+
+  template <typename I, typename V>
+  struct edge_to_color : Function_p2v< edge_to_color<I,V> >
+  {
+    typedef V result;
+
+    edge_to_color(const I& ima)
+      : ima_(ima)
+    {
+    }
+
+    V
+    operator()(const unsigned& e) const
+    {
+      return convert::to<V>(ima_.function()(e));
+    }
+
+    const I& ima_;
+  };
+
+  template <typename V>
+  inline
+  unsigned
+  find_root(util::array<V>& parent, const unsigned& x)
+  {
+    if (parent[x] == x)
+      return x;
+    else
+      return parent[x] = find_root(parent, parent[x]);
+  }
+
+
+  template <typename I, typename V, typename E>
+  inline
+  image2d<mln_value(I)>
+  make_debug_graph_image(const I& input,
+			 const V& ima_v, const E& ima_e,
+			 unsigned box_size, const value::int_u12& bg)
+  {
+    image2d<mln_value(I)> ima;
+    initialize(ima, input);
+
+    data::fill(ima, bg);
+    debug::draw_graph(ima, ima_v.domain(),
+		      pw::cst(150u),
+		      edge_to_color<E, mln_value(I)>(ima_e));
+
+    dpoint2d tl(-box_size,-box_size);
+    dpoint2d br(box_size,box_size);
+    mln_piter(V) p(ima_v.domain());
+    for_all(p)
+    {
+      box2d b(p + tl, p + br);
+      b.crop_wrt(ima.domain());
+      data::fill((ima | b).rw(), convert::to<mln_value(I)>(ima_v(p)));
+    }
+
+    return ima;
+  }
+
+}
+
+
+///////////////////
+//               //
+// Main Function //
+//               //
+///////////////////
+
+
+int main(int argc, char *argv[])
+{
+  using namespace mln;
+  using value::int_u8;
+  using value::int_u12;
+  using value::rgb8;
+  using value::label_16;
+
+  if (argc < 4)
+  {
+    std::cout << "Usage: " << argv[0] << " <ima.dcm> <closure_lambda> <box_size> <dist_max>"
+	      << std::endl;
+    return 1;
+  }
+
+  unsigned closure_lambda = atoi(argv[2]);
+  unsigned box_size = atoi(argv[3]);
+  unsigned dist_max = atoi(argv[4]);
+
+  image2d<int_u12> dcm;
+  io::dicom::load(dcm, argv[1]);
+
+  io::pgm::save(level::stretch(int_u8(), dcm), "wsd_01_src.pgm");
+
+  image2d<int_u12> grad = morpho::gradient(dcm, win_c4p());
+  image2d<int_u12> clo = morpho::closing::area(grad, c4(), closure_lambda);
+
+  label_16 nbasins;
+  image2d<label_16> wshed = morpho::meyer_wst(clo, c4(), nbasins);
+
+  io::pgm::save(level::stretch(int_u8(), clo), "wsd_02.pgm");
+  io::pgm::save(level::transform(wshed, fun::l2l::wrap<int_u8>()), "wsd_03.pgm");
+
+  mln_VAR(vol2_, morpho::elementary::dilation(extend(wshed | (pw::value(wshed) == 0u), wshed), c8()));
+
+  data::fill((wshed | (pw::value(wshed) == 0u)).rw(), vol2_);
+
+  io::pgm::save(level::transform(wshed, fun::l2l::wrap<int_u8>()), "wsd_04.pgm");
+
+  /// Build graph
+  util::graph g = make::graph(wshed, c4(), nbasins);
+  // Build graph images and compute distance values with a RGB image.
+  mln_VAR(ima_v, make_vertex_graph_image(g, dcm, wshed, nbasins));
+  mln_VAR(ima_e, make_edge_graph_image(ima_v, g));
+
+  /// Try to merge vertices.
+  mln_piter_(ima_e_t) e(ima_e.domain());
+  util::array<label_16> parent(g.v_nmax());
+  for (unsigned i = 0; i < parent.nelements(); ++i)
+    parent[i] = i;
+
+  for_all(e)
+  {
+    unsigned v1 = e.element().v1();
+    unsigned v2 = e.element().v2();
+    if (ima_e(e) <= dist_max && find_root(parent, v1) != find_root(parent, v2))
+      parent[find_root(parent, v1)] = find_root(parent, v2);
+  }
+
+  fun::i2v::array<label_16> f(parent.nelements());
+  std::vector<unsigned> new_label(parent.nelements(), 0);
+  unsigned nbasins2 = 0;
+  for (unsigned i = 0; i < parent.nelements(); ++i)
+  {
+    unsigned p = find_root(parent, i);
+    mln_assertion(parent[p] == find_root(parent, i));
+    if (new_label[p] == 0)
+      new_label[p] = nbasins2++;
+    f(i) = new_label[p];
+  }
+  mln_invariant(f(0) == 0u);
+  --nbasins2; // nbasins2 does not count the basin with label 0.
+  image2d<label_16> wsd2 = level::transform(wshed, f);
+
+  io::pgm::save(level::transform(wsd2, fun::l2l::wrap<int_u8>()), "wsd_05.pgm");
+
+  /// Reconstruct a graph from the simplified image.
+  util::graph g2 = make::graph(wsd2, c4(), nbasins2);
+
+  // Compute distance values with a RGB image.
+  mln_VAR(ima_v2, make_vertex_graph_image(g2, dcm, wsd2, nbasins2));
+  mln_VAR(ima_e2, make_edge_graph_image(ima_v2, g2));
+
+  mln_VAR(wsd2_, morpho::elementary::dilation(extend(wsd2 | (pw::value(wsd2) == 0u), wsd2), c8()));
+
+  data::fill((wsd2 | (pw::value(wsd2) == 0u)).rw(), wsd2_);
+
+  io::pgm::save(level::transform(labeling::mean_values(dcm, wsd2, nbasins2), fun::l2l::wrap<int_u8>()), "wsd_06_mean_colors.pgm");
+  io::pgm::save(level::stretch(int_u8(), make_debug_graph_image(dcm, ima_v2, ima_e2, box_size, 4095)), "wsd_07_graph_image2_white.pgm");
+  io::pgm::save(level::stretch(int_u8(), make_debug_graph_image(dcm, ima_v2, ima_e2, box_size, 0)), "wsd_08_graph_image2_black.pgm");
+  io::pgm::save(level::transform(wsd2, fun::l2l::wrap<int_u8>()), "wsd_99_result.pgm");
+}
Index: trunk/milena/sandbox/fabien/igr/check2d_wsd.sh
===================================================================
--- trunk/milena/sandbox/fabien/igr/check2d_wsd.sh	(revision 0)
+++ trunk/milena/sandbox/fabien/igr/check2d_wsd.sh	(revision 3503)
@@ -0,0 +1,32 @@
+#!/bin/zsh
+
+process_file ()
+{
+  echo "Processing $2..."
+  box_size=2
+
+  for lambda_closure in 10 50 100 500 1000; do
+    echo "  for lambda_closure = ${lambda_closure}";
+    
+    for dist_max in 5 10 50 100 500; do
+      echo "    for lambda_dist = ${dist_max}";
+
+      ./wsd2d $1 $lambda_closure $box_size $dist_max
+
+      mv wsd_01_src.pgm results/${2}_${lambda_closure}_${dist_max}_01.pgm
+      mv wsd_02.pgm results/${2}_${lambda_closure}_${dist_max}_02.pgm
+      mv wsd_03.pgm results/${2}_${lambda_closure}_${dist_max}_03.pgm
+      mv wsd_04.pgm results/${2}_${lambda_closure}_${dist_max}_04.pgm
+      mv wsd_05.pgm results/${2}_${lambda_closure}_${dist_max}_05.pgm
+      mv wsd_06_mean_colors.pgm results/${2}_${lambda_closure}_${dist_max}_06.pgm
+      mv wsd_07_graph_image2_white.pgm results/${2}_${lambda_closure}_${dist_max}_07.pgm
+      mv wsd_08_graph_image2_black.pgm results/${2}_${lambda_closure}_${dist_max}_08.pgm
+      mv wsd_99_result.pgm results/${2}_${lambda_closure}_${dist_max}_99.pgm
+
+      done;
+  done
+}
+
+process_file "/Users/HiSoKa/Work/IGR/souris18/irm/IM_0049.dcm" "49"
+process_file "/Users/HiSoKa/Work/IGR/souris18/irm/IM_0055.dcm" "55"
+process_file "/Users/HiSoKa/Work/IGR/souris18/irm/IM_0058.dcm" "58"
Property changes on: trunk/milena/sandbox/fabien/igr/check2d_wsd.sh
___________________________________________________________________
Name: svn:executable
   + *
Index: trunk/milena/sandbox/fabien/igr/check3d_wsd.sh
===================================================================
--- trunk/milena/sandbox/fabien/igr/check3d_wsd.sh	(revision 0)
+++ trunk/milena/sandbox/fabien/igr/check3d_wsd.sh	(revision 3503)
@@ -0,0 +1,34 @@
+#!/bin/zsh
+
+process_file ()
+{
+  echo "Processing $2..."
+  box_size=2
+
+  for lambda_closure in 10 50 100 500 1000; do
+    echo "  for lambda_closure = ${lambda_closure}";
+    
+    for dist_max in 5 10 50 100 500; do
+      echo "    for lambda_dist = ${dist_max}";
+
+      ./wsd3d $1 $lambda_closure $box_size $dist_max
+
+      ../bin/dump2pgm8b wsd_01_src.dump results/${2}_${lambda_closure}_${dist_max}_01.pgm
+      ../bin/dump2pgm8b wsd_02.dump results/${2}_${lambda_closure}_${dist_max}_02.pgm
+      ../bin/dump2pgm8b wsd_03.dump results/${2}_${lambda_closure}_${dist_max}_03.pgm
+      ../bin/dump2pgm8b wsd_04.dump results/${2}_${lambda_closure}_${dist_max}_04.pgm
+      ../bin/dump2pgm8b wsd_05.dump results/${2}_${lambda_closure}_${dist_max}_05.pgm
+      ../bin/dump2pgm8b wsd_06_mean_colors.dump results/${2}_${lambda_closure}_${dist_max}_06.pgm
+#../bin/dump2pgm8b wsd_07_graph_image2_white.dump results/${2}_${lambda_closure}_${dist_max}_07.pgm
+#../bin/dump2pgm8b wsd_08_graph_image2_black.dump results/${2}_${lambda_closure}_${dist_max}_08.pgm
+      ../bin/dump2pgm8b wsd_99_result.dump results/${2}_${lambda_closure}_${dist_max}_99.pgm
+
+      rm *.dump
+
+      done;
+  done
+}
+
+process_file "/Users/HiSoKa/Work/IGR/souris18/irm/IM_0052.dcm" "52"
+process_file "/Users/HiSoKa/Work/IGR/souris18/irm/IM_0061.dcm" "61"
+process_file "/Users/HiSoKa/Work/IGR/souris18/irm/IM_0064.dcm" "64"
Property changes on: trunk/milena/sandbox/fabien/igr/check3d_wsd.sh
___________________________________________________________________
Name: svn:executable
   + *
Index: trunk/milena/sandbox/fabien/igr/Makefile
===================================================================
--- trunk/milena/sandbox/fabien/igr/Makefile	(revision 3502)
+++ trunk/milena/sandbox/fabien/igr/Makefile	(revision 3503)
@@ -8,7 +8,7 @@
 	    -framework CoreFoundation
 CXXFLAGS = -DNDEBUG -O1
 
-all: 2d 3d wsd
+all: 2d 3d wsd2d wsd3d
 
 2d: seg_vol_irm.hh seg2d.cc
 	g++ -I../../../ ${DICOM_INC} ${DICOM_LIB} ${CXXFLAGS} seg2d.cc -o seg2d
@@ -16,8 +16,11 @@
 3d: seg_vol_irm.hh seg3d.cc
 	g++ -I../../../ ${DICOM_INC} ${DICOM_LIB} ${CXXFLAGS} seg3d.cc -o seg3d
 
-wsd: watershed.cc
-	g++ -I../../../ ${DICOM_INC} ${DICOM_LIB} ${CXXFLAGS} $^ -o wsd
+wsd2d: watershed.hh watershed2d.cc
+	g++ -I../../../ ${DICOM_INC} ${DICOM_LIB} ${CXXFLAGS} $^ -o wsd2d
+
+wsd3d: watershed.hh watershed3d.cc
+	g++ -I../../../ ${DICOM_INC} ${DICOM_LIB} ${CXXFLAGS} $^ -o wsd3d
 
 clean:
 	rm -rf *.dump *.p?m *.plot *.log *.csv
Index: trunk/milena/sandbox/fabien/TODO
===================================================================
--- trunk/milena/sandbox/fabien/TODO	(revision 3502)
+++ trunk/milena/sandbox/fabien/TODO	(revision 3503)
@@ -13,7 +13,7 @@
 [X] Generate histograms (normal, bg, obj, p_bg, p_obj)
 [ ] Test processing chain on US
 [X] Create README file for special images
-[ ] Implement watershed
+[X] Implement watershed
 [X] Create tool for dicom mask
 [X] Check conversion for mask (everything except int_u12(0))
 [ ] Extract ROI (projection or fill holes)
@@ -22,4 +22,7 @@
 [ ] After threshold, take biggest object and then erode, dilate
 [ ] Fix n_max
 [ ] Create routine for binary images (keep n big bg & m big objects)
-[ ] US: projection of internal gradient
+[X] US: projection of internal gradient
+[X] Create 3D US morpho with 2D stack
+[ ] Create macro for_all_slice
+[ ] Batch process watershed with 2D, 3D and any combination of parameters
Index: trunk/milena/sandbox/fabien/bin/dump2pgm.cc
===================================================================
--- trunk/milena/sandbox/fabien/bin/dump2pgm.cc	(revision 0)
+++ trunk/milena/sandbox/fabien/bin/dump2pgm.cc	(revision 3503)
@@ -0,0 +1,37 @@
+#include <mln/core/image/image2d.hh>
+#include <mln/make/image3d.hh>
+#include <mln/debug/slices_2d.hh>
+
+#include <mln/value/int_u8.hh>
+#include <mln/value/int_u12.hh>
+#include <mln/io/dump/load.hh>
+#include <mln/io/pgm/save.hh>
+
+#include <mln/literal/colors.hh>
+
+
+int usage(char* argv[])
+{
+  std::cerr << "usage: " << argv[0] << " input.dump output.pgm" << std::endl;
+  return 1;
+}
+
+
+
+int main(int argc, char* argv[])
+{
+  using namespace mln;
+  using value::rgb8;
+
+  if (argc != 3)
+    return usage(argv);
+
+  image3d<int_u12> vol;
+  io::dump::load(vol, argv[1]);
+
+  rgb8 bg = literal::black;
+  image2d<int_u8> ima = debug::slices_2d(vol, 1.f, bg);
+  io::pgm::save(ima, argv[2]);
+
+  return 0;
+}
Index: trunk/milena/sandbox/fabien/bin/filetype.hh
===================================================================
--- trunk/milena/sandbox/fabien/bin/filetype.hh	(revision 0)
+++ trunk/milena/sandbox/fabien/bin/filetype.hh	(revision 3503)
@@ -0,0 +1,54 @@
+#include <string>
+
+// 2d
+#include <mln/core/image/image2d.hh>
+#include <mln/core/alias/neighb2d.hh>
+
+// 3d
+#include <mln/core/image/image3d.hh>
+#include <mln/core/alias/neighb3d.hh>
+
+
+// pbm
+#include <mln/io/pbm/load.hh>
+#include <mln/io/pbm/save.hh>
+
+// pgm
+#include <mln/io/pgm/load.hh>
+#include <mln/io/pgm/save.hh>
+
+// ppm
+#include <mln/io/ppm/load.hh>
+#include <mln/io/ppm/save.hh>
+
+
+//dump
+#include <mln/io/dump/load.hh>
+#include <mln/io/dump/save.hh>
+
+
+namespace mln
+{
+
+  namespace filetype
+  {
+    enum id { pbm, pgm, ppm, dump, unknown };
+  }
+
+  filetype::id
+  get_filetype(const std::string& filename)
+  {
+    if (filename.find(".pbm") == filename.length() - 4)
+      return filetype::pbm;
+    if (filename.find(".pgm") == filename.length() - 4)
+      return filetype::pgm;
+    if (filename.find(".ppm") == filename.length() - 4)
+      return filetype::ppm;
+    if (filename.find(".dump") == filename.length() - 5)
+      return filetype::dump;
+    return filetype::unknown;
+  }
+
+} // mln
+
+
Index: trunk/milena/sandbox/fabien/bin/dump_mask.cc
===================================================================
--- trunk/milena/sandbox/fabien/bin/dump_mask.cc	(revision 0)
+++ trunk/milena/sandbox/fabien/bin/dump_mask.cc	(revision 3503)
@@ -0,0 +1,86 @@
+#include <mln/core/concept/image.hh>
+#include <mln/core/image/image2d.hh>
+#include <mln/core/image/image3d.hh>
+#include <mln/core/image/slice_image.hh>
+#include <mln/core/alias/neighb2d.hh>
+#include <mln/core/alias/neighb3d.hh>
+#include <mln/core/var.hh>
+
+#include <mln/geom/ncols.hh>
+#include <mln/geom/nrows.hh>
+
+#include <mln/value/int_u8.hh>
+#include <mln/io/dump/load.hh>
+#include <mln/io/pbm/save.hh>
+
+#include <mln/literal/colors.hh>
+#include <mln/morpho/elementary/gradient_internal.hh>
+
+#include <mln/level/transform.hh>
+#include <mln/fun/v2b/threshold.hh>
+
+#include <mln/data/fill.hh>
+#include <mln/pw/all.hh>
+
+
+int usage(char* argv[])
+{
+  std::cerr << "usage: " << argv[0] << " input.dump" << std::endl;
+  return 1;
+}
+
+
+
+int main(int argc, char* argv[])
+{
+  using namespace mln;
+  using value::int_u8;
+
+  if (argc != 2)
+    return usage(argv);
+
+  image3d<int_u8> input;
+  io::dump::load(input, argv[1]);
+
+  image3d<bool> ima = level::transform(input, fun::v2b::threshold<int_u8>(1));
+  mln_VAR(grad_int, morpho::elementary::gradient_internal(ima, c6()));
+
+  util::array<unsigned> xproj(ima.nrows(), 0);
+  util::array<unsigned> yproj(ima.ncols(), 0);
+  util::array<unsigned> xgradproj(grad_int.nrows(), 0);
+  util::array<unsigned> ygradproj(grad_int.ncols(), 0);
+
+  mln_piter_(image3d<bool>) p(ima.domain());
+  for_all(p)
+  {
+    if (ima(p))
+    {
+      ++xproj[p.row()];
+      ++yproj[p.col()];
+    }
+    if (grad_int(p))
+    {
+      ++xgradproj[p.row()];
+      ++ygradproj[p.col()];
+    }
+  }
+
+  // Plot files
+  std::ofstream fout_x("x.plot");
+  std::ofstream fout_xgrad("xgrad.plot");
+  for (unsigned int i = 0; i < xproj.nelements(); ++i)
+  {
+    fout_x << i << " " << xproj[i] << std::endl;
+    fout_xgrad << i << " " << xgradproj[i] << std::endl;
+  }
+
+  std::ofstream fout_y("y.plot");
+  std::ofstream fout_ygrad("ygrad.plot");
+  for (unsigned int i = 0; i < yproj.nelements(); ++i)
+  {
+    fout_y << i << " " << yproj[i] << std::endl;
+    fout_ygrad << i << " " << ygradproj[i] << std::endl;
+  }
+
+  return 0;
+}
Index: trunk/milena/sandbox/fabien/bin/dicom2pgm.cc
===================================================================
--- trunk/milena/sandbox/fabien/bin/dicom2pgm.cc	(revision 0)
+++ trunk/milena/sandbox/fabien/bin/dicom2pgm.cc	(revision 3503)
@@ -0,0 +1,34 @@
+#include <mln/core/concept/image.hh>
+#include <mln/core/image/image2d.hh>
+#include <mln/core/image/image3d.hh>
+
+#include <mln/value/int_u8.hh>
+#include <mln/value/int_u12.hh>
+#include <mln/io/dicom/load.hh>
+#include <mln/io/pgm/save.hh>
+
+
+
+int usage(char* argv[])
+{
+  std::cerr << "usage: " << argv[0] << " input.dcm output.dump" << std::endl;
+  std::cerr << "\t work for 2D images encoded in int_u8" << std::endl;
+  return 1;
+}
+
+
+int main(int argc, char* argv[])
+{
+  using namespace mln;
+  using value::int_u8;
+  using value::int_u12;
+
+  if (argc != 3)
+    return usage(argv);
+
+  image2d<int_u8> ima;
+  io::dicom::load(ima, argv[1]);
+  io::pgm::save(ima, argv[2]);
+
+  return 0;
+}
Index: trunk/milena/sandbox/fabien/bin/dump2pgm8b.cc
===================================================================
--- trunk/milena/sandbox/fabien/bin/dump2pgm8b.cc	(revision 0)
+++ trunk/milena/sandbox/fabien/bin/dump2pgm8b.cc	(revision 3503)
@@ -0,0 +1,38 @@
+#include <mln/core/image/image2d.hh>
+#include <mln/make/image3d.hh>
+#include <mln/debug/slices_2d.hh>
+
+#include <mln/value/int_u8.hh>
+#include <mln/value/int_u12.hh>
+#include <mln/io/dump/load.hh>
+#include <mln/io/pgm/save.hh>
+
+#include <mln/literal/colors.hh>
+
+
+int usage(char* argv[])
+{
+  std::cerr << "usage: " << argv[0] << " input.dump output.pgm" << std::endl;
+  return 1;
+}
+
+
+
+int main(int argc, char* argv[])
+{
+  using namespace mln;
+  using value::rgb8;
+  using value::int_u8;
+
+  if (argc != 3)
+    return usage(argv);
+
+  image3d<int_u8> vol;
+  io::dump::load(vol, argv[1]);
+
+  rgb8 bg = literal::black;
+  image2d<int_u8> ima = debug::slices_2d(vol, 1.f, 0);
+  io::pgm::save(ima, argv[2]);
+
+  return 0;
+}
Index: trunk/milena/sandbox/fabien/bin/pnms2dump.cc
===================================================================
--- trunk/milena/sandbox/fabien/bin/pnms2dump.cc	(revision 0)
+++ trunk/milena/sandbox/fabien/bin/pnms2dump.cc	(revision 3503)
@@ -0,0 +1,97 @@
+#include "filetype.hh"
+
+#include <mln/make/image3d.hh>
+
+
+
+
+void usage(char* argv[])
+{
+  std::cerr << "usage: " << argv[0] << " output.dump input1.xxx .. inputn.xxx" << std::endl;
+  std::cerr << "  It works with binary (pbm), gray-level (int_u8), and color (rgb8) images." << std::endl;
+  abort();
+}
+
+
+
+int main(int argc, char* argv[])
+{
+  using namespace mln;
+  using value::int_u8;
+
+  if (argc < 3)
+    usage(argv);
+
+  trace::entering("main");
+
+  std::string filename = argv[2];
+
+  switch (get_filetype(argv[2]))
+    {
+    case filetype::pbm:
+      {
+	typedef image2d<bool> I;
+	util::array<I> arr;
+	for (int i = 2; i < argc; ++i)
+	  {
+	    I ima;
+	    io::pbm::load(ima, argv[i]);
+	    arr.append(ima);
+	  }
+	image3d<bool> vol = make::image3d(arr);
+	io::dump::save(vol, argv[1]);
+      }
+      break;
+
+    case filetype::pgm:
+      {
+	using value::int_u8;
+	typedef image2d<int_u8> I;
+	util::array<I> arr;
+	for (int i = 2; i < argc; ++i)
+	  {
+	    I ima;
+	    io::pgm::load(ima, argv[i]);
+	    arr.append(ima);
+	  }
+	image3d<int_u8> vol = make::image3d(arr);
+	io::dump::save(vol, argv[1]);
+      }
+      break;
+
+    case filetype::ppm:
+      {
+	using value::rgb8;
+	typedef image2d<rgb8> I;
+	util::array<I> arr;
+	for (int i = 2; i < argc; ++i)
+	  {
+	    I ima;
+	    io::ppm::load(ima, argv[i]);
+	    arr.append(ima);
+	  }
+	image3d<rgb8> vol = make::image3d(arr);
+	io::dump::save(vol, argv[1]);
+      }
+      break;
+
+    case filetype::dump:
+      {
+	std::cerr << "Output shall not be a dump file!" << std::endl;
+	usage(argv);
+      }
+      break;
+
+    case filetype::unknown:
+      std::cerr << "unknown filename extension!" << std::endl;
+      usage(argv);
+      break;
+
+    default:
+      std::cerr << "file type not handled!" << std::endl;
+      usage(argv);
+    }
+
+  trace::exiting("main");
+
+}
                    
                  
                  
                          
                            
                            1
                            
                          
                          
                            
                            0
                            
                          
                          
                            
    
                          
                        
                    
                    
                          URL: https://svn.lrde.epita.fr/svn/oln/trunk
ChangeLog:
2009-03-09  Frederic Bour  <bour(a)lrde.epita.fr>
	Add missing trait for thru_morpher.
	* milena/sandbox/fred/fun/unary.hh: Add is_assignable trait, just
	a workaround.
---
 unary.hh |   17 +++++++++++++++++
 1 file changed, 17 insertions(+)
Index: trunk/milena/sandbox/fred/fun/unary.hh
===================================================================
--- trunk/milena/sandbox/fred/fun/unary.hh	(revision 3501)
+++ trunk/milena/sandbox/fred/fun/unary.hh	(revision 3502)
@@ -77,6 +77,23 @@
     };
   }
   
+  namespace trait
+  {
+    
+    namespace fun
+    {
+
+      /// Find correct implementation
+      template <typename F>
+      struct is_assignable
+      {
+	typedef metal::true_ ret;
+      };
+      
+    }
+    
+  }
+  
   namespace meta
   {
     template <template <class> class F>
                    
                  
                  
                          
                            
                            1
                            
                          
                          
                            
                            0
                            
                          
                          
                            
    
                          
                        
                    
                    
                          URL: https://svn.lrde.epita.fr/svn/oln/trunk
ChangeLog:
2009-03-09  Frederic Bour  <bour(a)lrde.epita.fr>
	Verify constness of "thru" image morpher.
	* milena/mln/trait/image/props.hh: Correct small typos errors.
	* milena/sandbox/fred/fun/abs.hh,
	* milena/sandbox/fred/fun/cos.hh,
	* milena/sandbox/fred/fun/inc.hh,
	* milena/sandbox/fred/fun/norm.hh,
	* milena/sandbox/fred/fun/red.hh:
	  Namespace name desambiguation.
	* milena/sandbox/fred/fun/thru.cc,
	* milena/sandbox/fred/fun/thru_morpher.hh:
	  Verify constness (WIP).
---
 mln/trait/image/props.hh         |    8 +--
 sandbox/fred/fun/abs.hh          |    4 -
 sandbox/fred/fun/cos.hh          |    4 -
 sandbox/fred/fun/inc.hh          |    4 -
 sandbox/fred/fun/norm.hh         |   12 ++--
 sandbox/fred/fun/red.hh          |    4 -
 sandbox/fred/fun/thru.cc         |    1 
 sandbox/fred/fun/thru_morpher.hh |   94 ++++++++++++++++++++++++++++++---------
 8 files changed, 92 insertions(+), 39 deletions(-)
Index: trunk/milena/mln/trait/image/props.hh
===================================================================
--- trunk/milena/mln/trait/image/props.hh	(revision 3500)
+++ trunk/milena/mln/trait/image/props.hh	(revision 3501)
@@ -393,7 +393,7 @@
       {
 	struct any { protected: any() {} };
 	struct slow : any { std::string name() const { return "speed::slow"; } };
-	struct fast : any { std::string name() const { return "speedw::fast"; } };
+	struct fast : any { std::string name() const { return "speed::fast"; } };
 	struct fastest
 	  : fast { std::string name() const { return "speed::fastest"; } };
       };
@@ -502,11 +502,11 @@
         struct any { protected: any() {} };
         struct some : any { protected: some() {} };
         struct read
-          : some { std::string name() const { return "pw_io::read"; } };
+          : some { std::string name() const { return "vw_io::read"; } };
         struct read_write
-          : some { std::string name() const { return "pw_io::read_write"; } };
+          : some { std::string name() const { return "vw_io::read_write"; } };
         struct none
-          : any { std::string name() const { return "pw_io::none"; } };
+          : any { std::string name() const { return "vw_io::none"; } };
       };
 
 // vw_set: /any/
Index: trunk/milena/sandbox/fred/fun/cos.hh
===================================================================
--- trunk/milena/sandbox/fred/fun/cos.hh	(revision 3500)
+++ trunk/milena/sandbox/fred/fun/cos.hh	(revision 3501)
@@ -18,7 +18,7 @@
   namespace trait
   {
     template <typename T>
-    struct set_unary_<fun::cos, mln::value::Floating, T>
+    struct set_unary_<mln::fun::cos, mln::value::Floating, T>
     {
       typedef set_unary_ ret;
       typedef T result;
@@ -39,7 +39,7 @@
 
   namespace meta
   {
-    typedef unary<fun::cos> cos;
+    typedef unary<mln::fun::cos> cos;
   }
 }
 
Index: trunk/milena/sandbox/fred/fun/abs.hh
===================================================================
--- trunk/milena/sandbox/fred/fun/abs.hh	(revision 3500)
+++ trunk/milena/sandbox/fred/fun/abs.hh	(revision 3501)
@@ -17,7 +17,7 @@
   namespace trait
   {
     template <typename T>
-    struct set_unary_<fun::abs, mln::value::Scalar, T>
+    struct set_unary_<mln::fun::abs, mln::value::Scalar, T>
     {
       typedef set_unary_ ret;
       typedef T result;
@@ -33,7 +33,7 @@
 
   namespace meta
   {
-    typedef unary<fun::abs> abs;
+    typedef unary<mln::fun::abs> abs;
   }
 }
 
Index: trunk/milena/sandbox/fred/fun/thru.cc
===================================================================
--- trunk/milena/sandbox/fred/fun/thru.cc	(revision 3500)
+++ trunk/milena/sandbox/fred/fun/thru.cc	(revision 3501)
@@ -8,6 +8,7 @@
 #include <iostream>
 
 #define dbg_print(val) std::cout << #val << "\n\t -> \t" << (val) << std::endl
+
 int main()
 {
   using namespace mln;
Index: trunk/milena/sandbox/fred/fun/inc.hh
===================================================================
--- trunk/milena/sandbox/fred/fun/inc.hh	(revision 3500)
+++ trunk/milena/sandbox/fred/fun/inc.hh	(revision 3501)
@@ -15,7 +15,7 @@
   namespace trait
   {
     template <typename T>
-    struct set_unary_<fun::inc, mln::value::Scalar, T>
+    struct set_unary_<mln::fun::inc, mln::value::Scalar, T>
     {
       typedef set_unary_ ret;
       typedef T result;
@@ -36,7 +36,7 @@
   
   namespace meta
   {
-    typedef unary<fun::inc> inc;
+    typedef unary<mln::fun::inc> inc;
   }
 }
 
Index: trunk/milena/sandbox/fred/fun/red.hh
===================================================================
--- trunk/milena/sandbox/fred/fun/red.hh	(revision 3500)
+++ trunk/milena/sandbox/fred/fun/red.hh	(revision 3501)
@@ -16,7 +16,7 @@
   namespace trait
   {
     template <unsigned n>
-    struct set_precise_unary_<fun::red, mln::value::rgb<n> >
+    struct set_precise_unary_<mln::fun::red, mln::value::rgb<n> >
     {
       typedef set_precise_unary_ ret;
       typedef mln::value::rgb<n> argument;
@@ -37,7 +37,7 @@
   
   namespace meta
   {
-    typedef unary<fun::red> red;
+    typedef unary<mln::fun::red> red;
   }
 }
 
Index: trunk/milena/sandbox/fred/fun/norm.hh
===================================================================
--- trunk/milena/sandbox/fred/fun/norm.hh	(revision 3500)
+++ trunk/milena/sandbox/fred/fun/norm.hh	(revision 3501)
@@ -25,7 +25,7 @@
   namespace trait
   {
     template <unsigned n, typename T>
-    struct set_precise_unary_<fun::norm::l1, mln::algebra::vec<n, T> >
+    struct set_precise_unary_<mln::fun::norm::l1, mln::algebra::vec<n, T> >
     {
       typedef set_precise_unary_ ret;
       typedef mln::algebra::vec<n, T> argument;
@@ -44,7 +44,7 @@
     };
 
     template <unsigned n, typename T>
-    struct set_precise_unary_<fun::norm::l2, mln::algebra::vec<n, T> >
+    struct set_precise_unary_<mln::fun::norm::l2, mln::algebra::vec<n, T> >
     {
       typedef set_precise_unary_ ret;
       typedef mln::algebra::vec<n, T> argument;
@@ -63,7 +63,7 @@
     };
 
     template <unsigned n, typename T>
-    struct set_precise_unary_<fun::norm::linfty, mln::algebra::vec<n, T> >
+    struct set_precise_unary_<mln::fun::norm::linfty, mln::algebra::vec<n, T> >
     {
       typedef set_precise_unary_ ret;
       typedef mln::algebra::vec<n, T> argument;
@@ -86,9 +86,9 @@
   {
     namespace norm
     {
-      typedef unary<fun::norm::l1> l1;
-      typedef unary<fun::norm::l2> l2;
-      typedef unary<fun::norm::linfty> linfty;
+      typedef unary<mln::fun::norm::l1> l1;
+      typedef unary<mln::fun::norm::l2> l2;
+      typedef unary<mln::fun::norm::linfty> linfty;
     }
   }
 }
Index: trunk/milena/sandbox/fred/fun/thru_morpher.hh
===================================================================
--- trunk/milena/sandbox/fred/fun/thru_morpher.hh	(revision 3500)
+++ trunk/milena/sandbox/fred/fun/thru_morpher.hh	(revision 3501)
@@ -30,7 +30,9 @@
 # define THRU_HH
 
 # include <mln/core/internal/image_value_morpher.hh>
+# include <mln/metal/bexpr.hh>
 # include "meta_function.hh"
+# include "unary.hh" // For is_assignable trait
 
 // FIXME: constness of thru_image
 
@@ -40,9 +42,23 @@
   // Forward declaration.
   template <typename I, typename F> struct thru_image;
 
-
   namespace internal
   {
+    template <typename I, typename F> struct thru_image_write;
+    template <typename I, typename F> struct thru_image_read;
+    
+    /// Find correct implementation
+    template <typename I, typename F>
+    struct thru_find_impl
+    {
+      typedef thru_image_write<I, F> write;
+      typedef thru_image_read<I, F> read;
+      typedef mlc_if(mlc_and(typename trait::fun::is_assignable<F>::ret,
+		     mlc_and(mlc_not(mlc_is_const(I)),
+			     mlc_equal(mln_trait_image_pw_io(I),
+				       trait::image::pw_io::read_write))),
+		     write, read) ret;
+    };
 
     /// Data structure for \c mln::thru_image<I>.
     template <typename I, typename F>
@@ -62,7 +78,7 @@
   {
 
     template <typename I, typename F>
-    struct image_< thru_image<I, F> > : image_< I > // Same as I except...
+    struct image_< thru_image<I, F> > : image_< typename mln::internal::thru_find_impl<I, F>::ret > // Same as I except...
     {
       // ...these changes.
       typedef trait::image::category::value_morpher category;
@@ -70,14 +86,29 @@
       typedef trait::image::value_access::computed value_access;
     };
 
+    template <typename I, typename F>
+    struct image_< mln::internal::thru_image_write<I, F> > : image_< I > // Same as I except...
+    {
+      typedef trait::image::vw_io::read_write vw_io;
+    };
+    
+    template <typename I, typename F>
+    struct image_< mln::internal::thru_image_read<I, F> > : image_< I > // Same as I except...
+    {
+      typedef trait::image::vw_io::read vw_io;
+    };
+    
   } // end of namespace mln::trait
 
 
 
   // FIXME: Doc!
 
+  namespace internal
+  {
+    
   template <typename I, typename F>
-  class thru_image : public internal::image_value_morpher< I, typename F::result, thru_image<I,F> >
+    class thru_image_read : public internal::image_value_morpher< I, typename F::result, thru_image<I,F> >
   {
   public:
 
@@ -90,23 +121,39 @@
     /// Value associated type.
     typedef typename F::result value;
 
+      /// Return type of read-only access.
+      typedef typename F::result rvalue;
+      
+      rvalue operator()(const mln_psite(I)& p) const;
+      
+    };
+    
+    // Inheritance from read ?!
+    template <typename I, typename F>
+    class thru_image_write : public thru_image_read<I,F>
+    {
+      public:
+
     /// Type returned by the read-write pixel value operator.
     typedef typename F::lresult lvalue;
 
-    /// Return type of read-only access.
-    typedef typename F::result rvalue;
+	using thru_image_read<I,F>::operator();
+	lvalue operator()(const mln_psite(I)& p);
+	
+    };
+  }
+  
+  template <typename I, typename F>
+  class thru_image : public internal::thru_find_impl<I, F>::ret
+  {
+  public:
     
     thru_image();
     thru_image(I& ima);
     thru_image(I& ima, const F& f);
 
-    // Initialize an empty image.
     void init_(I& ima, const F& f);
 
-    rvalue operator()(const mln_psite(I)& p) const;
-
-    lvalue operator()(const mln_psite(I)& p);
-
     /// Const promotion via conversion.
     operator thru_image<const I, F>() const;
   };
@@ -116,7 +163,7 @@
 			Image<I>& ima);
 
   template <typename I, typename F>
-  thru_image<const I, F> thru(const mln::Function<F>& f,
+  const thru_image<const I, F> thru(const mln::Function<F>& f,
 			      const Image<I>& ima);
 
   template <typename I, typename M>
@@ -124,7 +171,7 @@
   thru(const mln::Meta_Function<M>& f, Image<I>& ima);
 
   template <typename I, typename M>
-  thru_image<const I, mln_fun_with(M, mln_value(I))>
+  const thru_image<const I, mln_fun_with(M, mln_value(I))>
   thru(const mln::Meta_Function<M>& f, const Image<I>& ima);
 
 # ifndef MLN_INCLUDE_ONLY
@@ -180,17 +227,19 @@
 
   template <typename I, typename F>
   inline
-  typename thru_image<I, F>::rvalue
-  thru_image<I, F>::operator()(const mln_psite(I)& p) const
+  thru_image<I, F>::operator thru_image<const I, F>() const
   {
-    mln_precondition(this->is_valid());
-    return this->data_->f_(this->data_->ima_(p));
+    thru_image<const I, F> tmp(this->data_->ima_, this->data_->f_);
+    return tmp;
   }
 
+  namespace internal
+  {
+    
   template <typename I, typename F>
   inline
-  typename thru_image<I, F>::lvalue
-  thru_image<I, F>::operator()(const mln_psite(I)& p)
+    typename thru_image_read<I, F>::rvalue
+    thru_image_read<I, F>::operator()(const mln_psite(I)& p) const
   {
     mln_precondition(this->is_valid());
     return this->data_->f_(this->data_->ima_(p));    
@@ -198,10 +247,13 @@
 
   template <typename I, typename F>
   inline
-  thru_image<I, F>::operator thru_image<const I, F>() const
+    typename thru_image_write<I, F>::lvalue
+    thru_image_write<I, F>::operator()(const mln_psite(I)& p)
   {
-    thru_image<const I, F> tmp(this->data_->ima_, this->data_->default_value_);
-    return tmp;
+      mln_precondition(this->is_valid());
+      return this->data_->f_(this->data_->ima_(p));    
+    }
+    
   }
 
   // thru  
                    
                  
                  
                          
                            
                            1
                            
                          
                          
                            
                            0
                            
                          
                          
                            
    
                          
                        
                    
                    
                          URL: https://svn.lrde.epita.fr/svn/oln/trunk/milena/sandbox
ChangeLog:
2009-03-09  Fabien Freling  <fabien.freling(a)lrde.epita.fr>
	Fix dicom_mask binary.
	* fabien/bin/dicom_mask.cc: Fix bugs.
	* fabien/igr/watershed.cc: Update with watershed implementation.
---
 TODO              |    2 ++
 bin/dicom_mask.cc |   54 +++++++++++++++++++++++++++++++++++++++++++++++-------
 igr/watershed.cc  |   37 ++++++++++++++++++++++++-------------
 3 files changed, 73 insertions(+), 20 deletions(-)
Index: trunk/milena/sandbox/fabien/igr/watershed.cc
===================================================================
--- trunk/milena/sandbox/fabien/igr/watershed.cc	(revision 3499)
+++ trunk/milena/sandbox/fabien/igr/watershed.cc	(revision 3500)
@@ -52,6 +52,7 @@
 #include <mln/draw/box.hh>
 #include <mln/level/stretch.hh>
 #include <mln/fun/v2v/id.hh>
+#include <mln/fun/l2l/wrap.hh>
 #include <mln/core/image/line_graph_elt_neighborhood.hh>
 #include <mln/morpho/elementary/dilation.hh>
 #include <mln/labeling/mean_values.hh>
@@ -121,7 +122,7 @@
   {
     // Cf. sandbox/theo/color/segment_rgb_pixels.cc
 
-    util::array<vec3d_f> m_3f = labeling::compute(accu::mean<mln_value(I)>(),
+    util::array<float> m_3f = labeling::compute(accu::mean<mln_value(I)>(),
 						  input, // input color image
 						  w, // watershed labeling
 						  nbasins);
@@ -129,11 +130,11 @@
 
     util::array<mln_value(I)> m;
     convert::from_to(m_3f, m);
-    m[0] = literal::yellow;
+    m[0] = 150u;
 
-    io::ppm::save(level::transform(w,
+    /*io::ppm::save(level::transform(w,
 	  convert::to< fun::i2v::array<mln_value(I)> >(m)),
-	"wst_rag_wshd_color.ppm");
+	"wst_rag_wshd_color.ppm");*/
 
     return m;
   }
@@ -159,7 +160,7 @@
 
     typedef fun::i2v::array<mln_value(I)> vertex_values_t;
     vertex_values_t vertex_values;
-    // convert::from_to(mean_color_values(input, w, nbasins), vertex_values);
+    convert::from_to(mean_color_values(input, w, nbasins), vertex_values);
 
     mln_VAR(ima_v, (vertex_values | pv));
 
@@ -203,14 +204,14 @@
   image2d<mln_value(I)>
   make_debug_graph_image(const I& input,
 			 const V& ima_v, const E& ima_e,
-			 unsigned box_size, const value::rgb8& bg)
+			 unsigned box_size, const value::int_u12& bg)
   {
     image2d<mln_value(I)> ima;
     initialize(ima, input);
 
     data::fill(ima, bg);
     debug::draw_graph(ima, ima_v.domain(),
-		      pw::cst(mln_value(I)(literal::green)),
+		      pw::cst(150u),
 		      edge_to_color<E, mln_value(I)>(ima_e));
 
     dpoint2d tl(-box_size,-box_size);
@@ -246,26 +247,35 @@
 
   if (argc < 4)
   {
-    std::cout << "Usage: " << argv[0] << " <ima.dcm> <closure_lambda> <box_size>"
+    std::cout << "Usage: " << argv[0] << " <ima.dcm> <closure_lambda> <box_size> <dist_max>"
 	      << std::endl;
     return 1;
   }
 
+  unsigned closure_lambda = atoi(argv[2]);
   unsigned box_size = atoi(argv[3]);
+  unsigned dist_max = atoi(argv[4]);
 
   image2d<int_u12> dcm;
   io::dicom::load(dcm, argv[1]);
 
+  io::pgm::save(level::stretch(int_u8(), dcm), "wsd_01_src.pgm");
+
   image2d<int_u12> grad = morpho::gradient(dcm, win_c4p());
-  image2d<int_u12> clo = morpho::closing::area(grad, c4(), atoi(argv[2]));
+  image2d<int_u12> clo = morpho::closing::area(grad, c4(), closure_lambda);
 
   label_16 nbasins;
   image2d<label_16> wshed = morpho::meyer_wst(clo, c4(), nbasins);
 
+  io::pgm::save(level::stretch(int_u8(), clo), "wsd_02.pgm");
+  io::pgm::save(level::transform(wshed, fun::l2l::wrap<int_u8>()), "wsd_03.pgm");
+
   mln_VAR(vol2_, morpho::elementary::dilation(extend(wshed | (pw::value(wshed) == 0u), wshed), c8()));
 
   data::fill((wshed | (pw::value(wshed) == 0u)).rw(), vol2_);
 
+  io::pgm::save(level::transform(wshed, fun::l2l::wrap<int_u8>()), "wsd_04.pgm");
+
   /// Build graph
   util::graph g = make::graph(wshed, c4(), nbasins);
   // Build graph images and compute distance values with a RGB image.
@@ -282,8 +292,7 @@
   {
     unsigned v1 = e.element().v1();
     unsigned v2 = e.element().v2();
-    if (ima_e(e) <= (unsigned)(atoi(argv[5]))
-	&& find_root(parent, v1) != find_root(parent, v2))
+    if (ima_e(e) <= dist_max && find_root(parent, v1) != find_root(parent, v2))
       parent[find_root(parent, v1)] = find_root(parent, v2);
   }
 
@@ -302,6 +311,7 @@
   --nbasins2; // nbasins2 does not count the basin with label 0.
   image2d<label_16> wsd2 = level::transform(wshed, f);
 
+  io::pgm::save(level::transform(wsd2, fun::l2l::wrap<int_u8>()), "wsd_05.pgm");
 
   /// Reconstruct a graph from the simplified image.
   util::graph g2 = make::graph(wsd2, c4(), nbasins2);
@@ -314,7 +324,8 @@
 
   data::fill((wsd2 | (pw::value(wsd2) == 0u)).rw(), wsd2_);
 
+  io::pgm::save(level::transform(wsd2, fun::l2l::wrap<int_u8>()), "wsd_99_result.pgm");
   //io::ppm::save(labeling::mean_values(dcm, wsd2, nbasins2), "wst_rag_mean_colors.ppm");
-  //io::ppm::save(make_debug_graph_image(dcm, ima_v2, ima_e2, box_size, literal::white), "wst_rag_graph_image2_white.ppm");
-  //io::ppm::save(make_debug_graph_image(dcm, ima_v2, ima_e2, box_size, literal::black), "wst_rag_graph_image2_black.ppm");
+  io::pgm::save(make_debug_graph_image(dcm, ima_v2, ima_e2, box_size, 4095), "wsd_graph_image2_white.pgm");
+  io::pgm::save(make_debug_graph_image(dcm, ima_v2, ima_e2, box_size, 0), "wsd_graph_image2_black.pgm");
 }
Index: trunk/milena/sandbox/fabien/TODO
===================================================================
--- trunk/milena/sandbox/fabien/TODO	(revision 3499)
+++ trunk/milena/sandbox/fabien/TODO	(revision 3500)
@@ -21,3 +21,5 @@
 [X] Create dicom2dump (2d, 3d, int_u8, int_u12)
 [ ] After threshold, take biggest object and then erode, dilate
 [ ] Fix n_max
+[ ] Create routine for binary images (keep n big bg & m big objects)
+[ ] US: projection of internal gradient
Index: trunk/milena/sandbox/fabien/bin/dicom_mask.cc
===================================================================
--- trunk/milena/sandbox/fabien/bin/dicom_mask.cc	(revision 3499)
+++ trunk/milena/sandbox/fabien/bin/dicom_mask.cc	(revision 3500)
@@ -1,5 +1,7 @@
 #include <mln/core/concept/image.hh>
 #include <mln/core/image/image2d.hh>
+#include <mln/core/alias/neighb2d.hh>
+#include <mln/core/var.hh>
 
 #include <mln/geom/ncols.hh>
 #include <mln/geom/nrows.hh>
@@ -9,6 +11,7 @@
 #include <mln/io/pbm/save.hh>
 
 #include <mln/literal/colors.hh>
+#include <mln/morpho/elementary/gradient_internal.hh>
 
 #include <mln/level/transform.hh>
 #include <mln/fun/v2b/threshold.hh>
@@ -36,29 +39,66 @@
   image2d<int_u8> input;
   io::dicom::load(input, argv[1]);
 
-  util::array<unsigned> xproj(geom::nrows(input), 0);
-  util::array<unsigned> yproj(geom::ncols(input), 0);
+  util::array<unsigned> xsrcproj(geom::nrows(input), 0);
+  util::array<unsigned> ysrcproj(geom::ncols(input), 0);
 
-  mln_piter_(image2d<int_u8>) p(input.domain());
+  mln_piter_(image2d<int_u8>) psrc(input.domain());
+  for_all(psrc)
+  {
+    xsrcproj[psrc.row()] += input(psrc);
+    ysrcproj[psrc.col()] += input(psrc);
+  }
+  std::ofstream fout_xsrc("xsrc.plot");
+  for (unsigned int i = 0; i < xsrcproj.nelements(); ++i)
+  {
+    fout_xsrc << i << " " << xsrcproj[i] << std::endl;
+  }
+  std::ofstream fout_ysrc("ysrc.plot");
+  for (unsigned int i = 0; i < ysrcproj.nelements(); ++i)
+  {
+    fout_ysrc << i << " " << ysrcproj[i] << std::endl;
+  }
+
+  image2d<bool> ima = level::transform(input, fun::v2b::threshold<int_u8>(1));
+  io::pbm::save(ima, argv[2]);
+  mln_VAR(grad_int, morpho::elementary::gradient_internal(ima, c4()));
+
+  util::array<unsigned> xproj(geom::nrows(ima), 0);
+  util::array<unsigned> yproj(geom::ncols(ima), 0);
+  util::array<unsigned> xgradproj(geom::nrows(grad_int), 0);
+  util::array<unsigned> ygradproj(geom::ncols(grad_int), 0);
+
+  mln_piter_(image2d<bool>) p(ima.domain());
   for_all(p)
   {
-    xproj[p.row()] += input(p);
-    yproj[p.col()] += input(p);
+    if (ima(p))
+    {
+      ++xproj[p.row()];
+      ++yproj[p.col()];
+    }
+  }
+  mln_piter_(image2d<int_u8>) pgrad(grad_int.domain());
+  for_all(pgrad)
+  {
+    xgradproj[pgrad.row()] += grad_int(pgrad);
+    ygradproj[pgrad.col()] += grad_int(pgrad);
   }
 
   // Plot files
   std::ofstream fout_x("x.plot");
+  std::ofstream fout_xgrad("xgrad.plot");
   for (unsigned int i = 0; i < xproj.nelements(); ++i)
   {
     fout_x << i << " " << xproj[i] << std::endl;
+    fout_xgrad << i << " " << xgradproj[i] << std::endl;
   }
 
   std::ofstream fout_y("y.plot");
+  std::ofstream fout_ygrad("ygrad.plot");
   for (unsigned int i = 0; i < yproj.nelements(); ++i)
   {
     fout_y << i << " " << yproj[i] << std::endl;
+    fout_ygrad << i << " " << ygradproj[i] << std::endl;
   }
 
-  image2d<bool> ima = level::transform(input, fun::v2b::threshold<int_u8>(1));
-  io::pbm::save(ima, argv[2]);
 }
                    
                  
                  
                          
                            
                            1
                            
                          
                          
                            
                            0
                            
                          
                          
                            
    
                          
                        
                    
                    
                          URL: https://svn.lrde.epita.fr/svn/oln/trunk/milena
ChangeLog:
2009-03-08  Frederic Bour  <bour(a)lrde.epita.fr>
	Add work on meta functions and thru morpher.
	* sandbox/fred/fun/abs.hh: New.
	* sandbox/fred/fun/cos.hh: New.
	* sandbox/fred/fun/fun.cc: New.
	  Some tests don't pass, see FIXMEs (operator==(rgb::red_t,int) ?)
	* sandbox/fred/fun/inc.hh: New.
	* sandbox/fred/fun/meta_function.hh: New.
	* sandbox/fred/fun/norm.hh: New.
	* sandbox/fred/fun/red.hh: New.
	* sandbox/fred/fun/thru.cc: New.
	* sandbox/fred/fun/thru_morpher.hh: New.
	  Constness of thru_image has to be corrected.
	* sandbox/fred/fun/unary.hh: New.
	* sandbox/fred/fun: New.
---
 abs.hh           |   40 ++++++++
 cos.hh           |   46 ++++++++++
 fun.cc           |  103 ++++++++++++++++++++++
 inc.hh           |   43 +++++++++
 meta_function.hh |  132 +++++++++++++++++++++++++++++
 norm.hh          |   96 +++++++++++++++++++++
 red.hh           |   44 +++++++++
 thru.cc          |   30 ++++++
 thru_morpher.hh  |  249 +++++++++++++++++++++++++++++++++++++++++++++++++++++++
 unary.hh         |  115 +++++++++++++++++++++++++
 10 files changed, 898 insertions(+)
Index: trunk/milena/sandbox/fred/fun/cos.hh
===================================================================
--- trunk/milena/sandbox/fred/fun/cos.hh	(revision 0)
+++ trunk/milena/sandbox/fred/fun/cos.hh	(revision 3498)
@@ -0,0 +1,46 @@
+#ifndef COS_HH
+# define COS_HH
+
+# include "unary.hh"
+# include <mln/value/concept/floating.hh>
+# include <mln/math/acos.hh>
+# include <mln/math/cos.hh>
+
+namespace mln
+{
+  // COS, bijective
+  namespace fun
+  {
+    template <typename T>
+    struct cos : unary<cos, T> {};
+  }
+
+  namespace trait
+  {
+    template <typename T>
+    struct set_unary_<fun::cos, mln::value::Floating, T>
+    {
+      typedef set_unary_ ret;
+      typedef T result;
+      typedef T argument;
+      typedef T& lvalue;
+      
+      static result read(const argument& x)
+      {
+	return math::cos(x);
+      }
+
+      static void write(lvalue l, const result& x)
+      {
+	l = math::acos(x);
+      }
+    };
+  }
+
+  namespace meta
+  {
+    typedef unary<fun::cos> cos;
+  }
+}
+
+#endif /* ! COS_HH */
\ No newline at end of file
Index: trunk/milena/sandbox/fred/fun/abs.hh
===================================================================
--- trunk/milena/sandbox/fred/fun/abs.hh	(revision 0)
+++ trunk/milena/sandbox/fred/fun/abs.hh	(revision 3498)
@@ -0,0 +1,40 @@
+#ifndef ABS_HH
+# define ABS_HH
+
+# include "unary.hh"
+# include <mln/value/concept/scalar.hh>
+# include <mln/math/abs.hh>
+
+namespace mln
+{
+  // ABS, pure
+  namespace fun
+  {
+    template <typename T>
+    struct abs : unary<abs, T> {};
+  }
+
+  namespace trait
+  {
+    template <typename T>
+    struct set_unary_<fun::abs, mln::value::Scalar, T>
+    {
+      typedef set_unary_ ret;
+      typedef T result;
+      typedef T argument;
+      typedef T& lvalue;
+      
+      static result read(const argument& x)
+      {
+	return math::abs(x);
+      }
+    };
+  }
+
+  namespace meta
+  {
+    typedef unary<fun::abs> abs;
+  }
+}
+
+#endif /* ! ABS_HH */
\ No newline at end of file
Index: trunk/milena/sandbox/fred/fun/thru.cc
===================================================================
--- trunk/milena/sandbox/fred/fun/thru.cc	(revision 0)
+++ trunk/milena/sandbox/fred/fun/thru.cc	(revision 3498)
@@ -0,0 +1,30 @@
+// Meta functions test
+#include "cos.hh"
+#include "thru_morpher.hh"
+
+#include <mln/core/image/image2d.hh>
+#include <mln/value/int_u8.hh>
+#include <mln/debug/all.hh>
+#include <iostream>
+
+#define dbg_print(val) std::cout << #val << "\n\t -> \t" << (val) << std::endl
+int main()
+{
+  using namespace mln;
+
+  meta::cos cos;
+  typedef image2d<float> I;
+  I ima(5, 5);
+
+  image2d<value::int_u8> tmp(5, 5);
+  debug::iota(tmp);
+  data::fill_with_image(ima, tmp);
+
+  debug::println(ima);
+  debug::println(thru(meta::cos(), ima));
+
+  thru_image<I, mln::fun::cos<float> > ima2 = thru(meta::cos(), ima);
+  data::fill_with_image(ima2, (pw::value(tmp) - pw::cst(13.0f)) / pw::cst(12.0f) | tmp.domain());
+
+  debug::println(ima);
+}
\ No newline at end of file
Index: trunk/milena/sandbox/fred/fun/inc.hh
===================================================================
--- trunk/milena/sandbox/fred/fun/inc.hh	(revision 0)
+++ trunk/milena/sandbox/fred/fun/inc.hh	(revision 3498)
@@ -0,0 +1,43 @@
+#ifndef INC_HH
+# define INC_HH
+
+# include "unary.hh"
+
+namespace mln
+{
+  // INC, bijective
+  namespace fun
+  {
+    template <typename T>
+    struct inc : unary<inc, T> {};
+  }
+
+  namespace trait
+  {
+    template <typename T>
+    struct set_unary_<fun::inc, mln::value::Scalar, T>
+    {
+      typedef set_unary_ ret;
+      typedef T result;
+      typedef T argument;
+      typedef T& lvalue;
+      
+      static result read(const argument& x)
+      {
+	return x + 1;
+      }
+      
+      static void write(lvalue l, const result& r)
+      {
+	l = r - 1;
+      }
+    };
+  } 
+  
+  namespace meta
+  {
+    typedef unary<fun::inc> inc;
+  }
+}
+
+#endif /* ! INC_HH */
\ No newline at end of file
Index: trunk/milena/sandbox/fred/fun/red.hh
===================================================================
--- trunk/milena/sandbox/fred/fun/red.hh	(revision 0)
+++ trunk/milena/sandbox/fred/fun/red.hh	(revision 3498)
@@ -0,0 +1,44 @@
+#ifndef RED_HH
+# define RED_HH
+
+# include "unary.hh"
+# include <mln/value/rgb.hh>
+
+namespace mln
+{
+  // RED, assignable
+  namespace fun
+  {
+    template <typename T>
+    struct red : unary<red, T> {};
+  }
+
+  namespace trait
+  {
+    template <unsigned n>
+    struct set_precise_unary_<fun::red, mln::value::rgb<n> >
+    {
+      typedef set_precise_unary_ ret;
+      typedef mln::value::rgb<n> argument;
+      typedef typename argument::red_t result;
+      typedef argument& lvalue;
+      
+      static result read(const argument& x)
+      {
+	return x.red();
+      }
+      
+      static void write(lvalue l, const result& r)
+      {
+	l.red() = r;
+      }
+    };
+  }
+  
+  namespace meta
+  {
+    typedef unary<fun::red> red;
+  }
+}
+
+#endif /* ! RED_HH */
\ No newline at end of file
Index: trunk/milena/sandbox/fred/fun/meta_function.hh
===================================================================
--- trunk/milena/sandbox/fred/fun/meta_function.hh	(revision 0)
+++ trunk/milena/sandbox/fred/fun/meta_function.hh	(revision 3498)
@@ -0,0 +1,132 @@
+// Copyright (C) 2007, 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 F 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.
+
+#ifndef MLN_CORE_CONCEPT_META_FUNCTION_HH
+# define MLN_CORE_CONCEPT_META_FUNCTION_HH
+
+/// \file mln/core/concept/meta_function.hh
+///
+/// Definition of the concept of mln::Meta_Function.
+
+# include <mln/core/concept/object.hh>
+# include <mln/core/concept/function.hh>
+
+
+# define mln_fun_with(F, T) \
+typename F::template with< T >::ret
+
+# define mln_fun_with_(F, T) \
+F::with< T >::ret
+
+
+# define mln_fun_result(F, T) \
+typename F::template with< T >::ret::result
+
+
+# define mln_fun_result_(F, T) \
+F::with< T >::ret::result
+
+
+
+namespace mln
+{
+
+  // Fwd decl.
+  template <typename E> struct Meta_Function;
+
+  // Meta_Function category flag type.
+  template <>
+  struct Meta_Function<void>
+  {
+    typedef Object<void> super;
+  };
+
+
+  /*! \brief Base class for implementation of meta functions.
+   *
+   * The parameter \a E is the exact type.
+   *
+   * \see mln::doc::Meta_Function for a complete documentation of
+   * this class contents.
+   */
+  template <typename E>
+  struct Meta_Function : public Object<E>
+  {
+    typedef Meta_Function<void> category;
+  protected:
+    Meta_Function();
+  };
+
+
+  namespace fun
+  {
+
+    // To be specialized when some state (attributes) have to be transfered
+    // from the meta-function to the function.
+    // Warning: the first argument has to be an object with the exact type.
+    template <typename M, typename T>
+    mln_fun_with(M, T)
+    unmeta(const M&, T);
+
+    template <typename M, typename T>
+    void
+    unmeta(const Meta_Function<M>&, T); // Safety.
+
+  } // end of namespace mln::fun
+
+
+
+# ifndef MLN_INCLUDE_ONLY
+
+  template <typename E>
+  inline
+  Meta_Function<E>::Meta_Function()
+  {
+    // FIXME: Check "with" on E.
+  }
+
+  namespace fun
+  {
+
+    template <typename M, typename T>
+    inline
+    mln_fun_with(M, T)
+    unmeta(const M&, T)
+    {
+      mlc_is_a(M, Meta_Function)::check();
+      mln_fun_with(M, T) a;
+      return a;
+    }
+
+  } // end of namespace mln::fun
+
+# endif // ! MLN_INCLUDE_ONLY
+
+} // end of namespace mln
+
+
+#endif // ! MLN_CORE_CONCEPT_META_FUNCTION_HH
Index: trunk/milena/sandbox/fred/fun/norm.hh
===================================================================
--- trunk/milena/sandbox/fred/fun/norm.hh	(revision 0)
+++ trunk/milena/sandbox/fred/fun/norm.hh	(revision 3498)
@@ -0,0 +1,96 @@
+#ifndef NORM_HH
+# define NORM_HH
+
+# include "unary.hh"
+# include <mln/norm/all.hh>
+
+namespace mln
+{
+  // NORMS, reversible
+  namespace fun
+  {
+    namespace norm
+    {
+      template <typename T>
+      struct l1 : unary<l1, T> {};
+
+      template <typename T>
+      struct l2 : unary<l2, T> {};
+
+      template <typename T>
+      struct linfty : unary<linfty, T> {};
+    }
+  }
+
+  namespace trait
+  {
+    template <unsigned n, typename T>
+    struct set_precise_unary_<fun::norm::l1, mln::algebra::vec<n, T> >
+    {
+      typedef set_precise_unary_ ret;
+      typedef mln::algebra::vec<n, T> argument;
+      typedef argument& lvalue;
+      typedef mln_sum_product(argument,argument) result;
+      
+      static result read(const argument& x)
+      {
+	return mln::norm::l1(x);
+      }
+      
+      static void write(lvalue l, const result& x)
+      {
+	l = l / read(l) * x;
+      }
+    };
+
+    template <unsigned n, typename T>
+    struct set_precise_unary_<fun::norm::l2, mln::algebra::vec<n, T> >
+    {
+      typedef set_precise_unary_ ret;
+      typedef mln::algebra::vec<n, T> argument;
+      typedef argument& lvalue;
+      typedef mln_sum_product(argument,argument) result;
+      
+      static result read(const argument& x)
+      {
+	return mln::norm::l2(x);
+      }
+      
+      static void write(lvalue l, const result& x)
+      {
+	l = l / read(l) * x;
+      }
+    };
+
+    template <unsigned n, typename T>
+    struct set_precise_unary_<fun::norm::linfty, mln::algebra::vec<n, T> >
+    {
+      typedef set_precise_unary_ ret;
+      typedef mln::algebra::vec<n, T> argument;
+      typedef argument& lvalue;
+      typedef mln_sum_product(argument,argument) result;
+      
+      static result read(const argument& x)
+      {
+	return mln::norm::linfty(x);
+      }
+      
+      static void write(lvalue l, const result& x)
+      {
+	l = l / read(l) * x;
+      }
+    };
+  } 
+  
+  namespace meta
+  {
+    namespace norm
+    {
+      typedef unary<fun::norm::l1> l1;
+      typedef unary<fun::norm::l2> l2;
+      typedef unary<fun::norm::linfty> linfty;
+    }
+  }
+}
+
+#endif /* ! NORM_HH */
\ No newline at end of file
Index: trunk/milena/sandbox/fred/fun/fun.cc
===================================================================
--- trunk/milena/sandbox/fred/fun/fun.cc	(revision 0)
+++ trunk/milena/sandbox/fred/fun/fun.cc	(revision 3498)
@@ -0,0 +1,103 @@
+// Meta functions test
+#include "abs.hh"
+#include "cos.hh"
+#include "inc.hh"
+#include "norm.hh"
+#include "red.hh"
+
+#include <iostream>
+
+#define dbg_print(val) std::cout << #val << "\n\t -> \t" << (val) << std::endl
+int main()
+{
+  mln::meta::abs abs;
+  mln::meta::cos cos;
+  mln::meta::inc inc;
+  mln::meta::red red;
+
+  mln::meta::norm::l1 l1;
+  mln::meta::norm::l2 l2;
+  mln::meta::norm::linfty linfty;
+
+  std::cout << "Read only tests." << std::endl;
+  std::cout << "----------------" << std::endl;
+
+  // ABS
+  mln_invariant(abs(-1) == 1);
+  dbg_print(abs(-1));
+  dbg_print(abs(-3.1415926535));
+
+  // INC
+  mln_invariant(inc(-1) == 0);
+  dbg_print(inc(-1));
+  dbg_print(inc(-3.1415926535));
+
+  // COS
+  mln_invariant(cos(0.) == 1.);
+  dbg_print(cos(0.));
+  dbg_print(cos(mln::math::acos(0.5)));
+
+  // RED
+  mln_invariant(red(mln::value::rgb8(8,13,21)) == 8);
+  dbg_print(red(mln::value::rgb8(8,13,21)));
+
+  // NORM
+  mln::algebra::vec<3, double> v;
+  v[0] = 1;
+  v[1] = -1;
+  v[2] = 0;
+  dbg_print(v);
+  dbg_print(l1(v));
+  dbg_print(l2(v));
+  dbg_print(linfty(v));
+
+  std::cout << "Read/Write tests." << std::endl;
+  std::cout << "-----------------" << std::endl;
+
+  // INC
+  {
+    int x;
+    dbg_print(inc(x) = 1);
+    mln_invariant(inc(x) == 1);
+    dbg_print(inc(x));
+    dbg_print(x);
+  }
+
+  // COS
+  {
+    double x;
+    dbg_print(cos(x) = 1.);
+    mln_invariant(cos(x) == 1.);
+    dbg_print(x);
+  }
+
+  // RED
+  {
+    mln::value::rgb8 rgb(8,13,21);
+    dbg_print(rgb);
+    dbg_print(red(rgb) = 0);
+// FIXME: Doesn't compile! mln_invariant(red(rgb) == 0);
+    dbg_print(rgb);
+  }
+
+  // NORM
+  {
+    dbg_print(v);
+    dbg_print(l1(v) = 1.0);
+    dbg_print(l1(v));
+    dbg_print(v);
+//     mln_invariant(l1(v) == 1.0); FIXME: check with epsilon
+
+    dbg_print(l2(v) = 1.0);
+    dbg_print(l2(v));
+    dbg_print(v);
+//     mln_invariant(l2(v) == 1.0);
+
+    dbg_print(linfty(v) = 1.0);
+    dbg_print(linfty(v));
+    dbg_print(v);
+//     mln_invariant(linfty(v) == 1.0);
+  }
+
+  std::cout << "All invariants passed." << std::endl;
+}
\ No newline at end of file
Index: trunk/milena/sandbox/fred/fun/thru_morpher.hh
===================================================================
--- trunk/milena/sandbox/fred/fun/thru_morpher.hh	(revision 0)
+++ trunk/milena/sandbox/fred/fun/thru_morpher.hh	(revision 3498)
@@ -0,0 +1,249 @@
+// Copyright (C) 2007, 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.
+
+#ifndef THRU_HH
+# define THRU_HH
+
+# include <mln/core/internal/image_value_morpher.hh>
+# include "meta_function.hh"
+
+// FIXME: constness of thru_image
+
+namespace mln
+{
+
+  // Forward declaration.
+  template <typename I, typename F> struct thru_image;
+
+
+  namespace internal
+  {
+
+    /// Data structure for \c mln::thru_image<I>.
+    template <typename I, typename F>
+    struct data< thru_image<I, F> >
+    {
+      data(I& ima, const F& f);
+
+      I ima_;
+      // FIXME: value or pointer or whatever ?
+      F f_;
+    };
+
+  } // end of namespace mln::internal
+
+
+  namespace trait
+  {
+
+    template <typename I, typename F>
+    struct image_< thru_image<I, F> > : image_< I > // Same as I except...
+    {
+      // ...these changes.
+      typedef trait::image::category::value_morpher category;
+      typedef mln_internal_trait_image_speed_from(I) speed; // Un-fastest.
+      typedef trait::image::value_access::computed value_access;
+    };
+
+  } // end of namespace mln::trait
+
+
+
+  // FIXME: Doc!
+
+  template <typename I, typename F>
+  class thru_image : public internal::image_value_morpher< I, typename F::result, thru_image<I,F> >
+  {
+  public:
+
+    /// Skeleton.
+    typedef thru_image<tag::image_<I>, F> skeleton;
+    
+    /// Point_Site associated type.
+    typedef mln_psite(I) psite;
+
+    /// Value associated type.
+    typedef typename F::result value;
+
+    /// Type returned by the read-write pixel value operator.
+    typedef typename F::lresult lvalue;
+
+    /// Return type of read-only access.
+    typedef typename F::result rvalue;
+    
+    thru_image();
+    thru_image(I& ima);
+    thru_image(I& ima, const F& f);
+
+    // Initialize an empty image.
+    void init_(I& ima, const F& f);
+
+    rvalue operator()(const mln_psite(I)& p) const;
+
+    lvalue operator()(const mln_psite(I)& p);
+
+    /// Const promotion via conversion.
+    operator thru_image<const I, F>() const;
+  };
+
+  template <typename I, typename F>
+  thru_image<I, F> thru(const mln::Function<F>& f,
+			Image<I>& ima);
+
+  template <typename I, typename F>
+  thru_image<const I, F> thru(const mln::Function<F>& f,
+			      const Image<I>& ima);
+
+  template <typename I, typename M>
+  thru_image<I, mln_fun_with(M, mln_value(I))>
+  thru(const mln::Meta_Function<M>& f, Image<I>& ima);
+
+  template <typename I, typename M>
+  thru_image<const I, mln_fun_with(M, mln_value(I))>
+  thru(const mln::Meta_Function<M>& f, const Image<I>& ima);
+
+# ifndef MLN_INCLUDE_ONLY
+
+  // internal::data< thru_image<I,S> >
+
+  namespace internal
+  {
+
+    template <typename I, typename F>
+    inline
+    data< thru_image<I, F> >::data(I& ima, const F& f)
+      : ima_(ima),
+	f_(f)
+    {
+    }
+
+  } // end of namespace mln::internal
+
+  // thru_image<I>
+
+  template <typename I, typename F>
+  inline
+  thru_image<I, F>::thru_image()
+  {
+  }
+
+  template <typename I, typename F>
+  inline
+  thru_image<I, F>::thru_image(I& ima, const F& f)
+  {
+    mln_precondition(ima.is_valid());
+    init_(ima, f);
+  }
+
+  template <typename I, typename F>
+  inline
+  thru_image<I, F>::thru_image(I& ima)
+  {
+    mln_precondition(ima.is_valid());
+    init_(ima, mln_value(I)());
+  }
+
+  template <typename I, typename F>
+  inline
+  void
+  thru_image<I, F>::init_(I& ima, const F& f)
+  {
+    mln_precondition(! this->is_valid());
+    mln_precondition(ima.is_valid());
+    this->data_ = new internal::data< thru_image<I, F> >(ima, f);
+  }
+
+  template <typename I, typename F>
+  inline
+  typename thru_image<I, F>::rvalue
+  thru_image<I, F>::operator()(const mln_psite(I)& p) const
+  {
+    mln_precondition(this->is_valid());
+    return this->data_->f_(this->data_->ima_(p));
+  }
+
+  template <typename I, typename F>
+  inline
+  typename thru_image<I, F>::lvalue
+  thru_image<I, F>::operator()(const mln_psite(I)& p)
+  {
+    mln_precondition(this->is_valid());
+    return this->data_->f_(this->data_->ima_(p));    
+  }
+
+  template <typename I, typename F>
+  inline
+  thru_image<I, F>::operator thru_image<const I, F>() const
+  {
+    thru_image<const I, F> tmp(this->data_->ima_, this->data_->default_value_);
+    return tmp;
+  }
+
+  // thru  
+  template <typename I, typename F>
+  thru_image<I, F> thru(const mln::Function<F>& f,
+			Image<I>& ima)
+  {
+    thru_image<I, F> tmp(exact(ima), exact(f));
+    return tmp;
+  }
+
+  template <typename I, typename F>
+  thru_image<const I, F> thru(const mln::Function<F>& f,
+			      const Image<I>& ima)
+  {
+    thru_image<const I, F> tmp(exact(ima), exact(f));
+    return tmp;
+  }
+
+  template <typename I, typename M>
+  thru_image<I, mln_fun_with(M, mln_value(I))>
+  thru(const mln::Meta_Function<M>& f, Image<I>& ima)
+  {
+    typedef mln_fun_with(M, mln_value(I)) F;
+    thru_image<I, F> tmp(exact(ima), F());
+
+    return tmp;
+  }
+
+  template <typename I, typename M>
+  thru_image<const I, mln_fun_with(M, mln_value(I))>
+  thru(const mln::Meta_Function<M>& f, const Image<I>& ima)
+  {
+    typedef mln_fun_with(M, mln_value(I)) F;
+    thru_image<const I, F> tmp(exact(ima), F());
+
+    return tmp;
+  }
+  
+# endif // ! MLN_INCLUDE_ONLY
+
+} // end of namespace mln
+
+
+#endif // ! THRU_HH
Index: trunk/milena/sandbox/fred/fun/unary.hh
===================================================================
--- trunk/milena/sandbox/fred/fun/unary.hh	(revision 0)
+++ trunk/milena/sandbox/fred/fun/unary.hh	(revision 3498)
@@ -0,0 +1,115 @@
+#ifndef UNARY_HH
+# define UNARY_HH
+
+# include <mln/trait/solve.hh>
+# include <mln/fun/essential.hh>
+# include <mln/fun/internal/resolve.hh>
+# include "meta_function.hh"
+
+namespace mln
+{
+  // UNARY
+  namespace fun
+  {
+    namespace internal
+    {
+      template <typename Impl>
+      struct unary_modifier
+      {
+	typedef typename Impl::result   result;
+	typedef typename Impl::argument argument;
+	typedef typename Impl::lvalue   lvalue;
+	typedef unary_modifier          lresult;
+	
+	// FIXME: argument or lvalue? ~~~
+	unary_modifier(argument& x)
+	: x_(&x)
+	{
+	}
+	
+	result to_result() const
+	{
+	  return Impl::read(*x_);
+	};
+	
+	operator result() const
+	{
+	  return to_result();
+	};
+
+	const result& operator = (const result& r) const
+	{
+	  Impl::write(*x_, r);
+	  return r;
+	}
+	
+      private:
+	argument *x_;
+      };
+    }
+
+    template <template <class> class Fun, typename T>
+    struct unary : mln::Function_v2v< Fun<T> >
+    {
+      // FIXME: mln_fun_internal_resolve? Fun<T> is not defined at this point...
+      //	so mln_is_a(Fun<T>, Function) won't work.
+      typedef typename mln::trait::solve_unary< Fun, T >::ret impl;
+      
+      typedef typename impl::result          result;
+      typedef typename impl::argument        argument;
+      typedef typename impl::lvalue          lvalue;
+      typedef internal::unary_modifier<impl> lresult;
+      
+      result operator () (const argument& value) const
+      {
+	return impl::read(value);
+      }
+      
+      lresult operator () (argument& value) const
+      {
+	return lresult(value);
+      }
+      
+      void set(lvalue l, const result& r) const
+      {
+	impl::write(l, r);
+      }
+    };
+  }
+  
+  namespace meta
+  {
+    template <template <class> class F>
+    struct unary : mln::Meta_Function< unary<F> >
+    {
+      template <typename T>
+      typename F<T>::result operator()(const T& v) const
+      {
+	F<T> tmp;
+	return tmp(v);
+      }
+      
+      template <typename T>
+      typename F<T>::lresult operator()(T& v) const
+      {
+	F<T> tmp;
+	return tmp(v);
+      }
+      
+      template <typename T>
+      struct with
+      {
+	typedef F<T> ret;
+      };
+    };    
+  }
+  
+}
+
+template <typename Impl>
+std::ostream& operator << (std::ostream& o, const mln::fun::internal::unary_modifier<Impl>& m)
+{
+  return o << m.to_result();
+}
+
+#endif /* ! UNARY_HH */
\ No newline at end of file
                    
                  
                  
                          
                            
                            1
                            
                          
                          
                            
                            0
                            
                          
                          
                            
    
                          
                        
                    
                        
                            
                                
                            
                            r3497: Add routine to calculate the number of	components retaled to a createria
                        
                        
by Edwin Carlinet 06 Mar '09
                    by Edwin Carlinet 06 Mar '09
06 Mar '09
                    
                          URL: https://svn.lrde.epita.fr/svn/oln/trunk/milena/sandbox
ChangeLog:
2009-03-06  Edwin Carlinet  <carlinet(a)lrde.epita.fr>
	Add routine to calculate the number of components retaled to a createria.
	* edwin/tree/propagate.hh: Back propagation iterates on nodes now.
	* edwin/tree/routines.hh: Add routine to get hightest nodes
	satifying a criteria .
	* edwin/tree/tree.cc: Add function to calcul number of
	components retaled to a createria (in expectation of finding a
	treshold).
---
 propagate.hh |   13 +++--
 routines.hh  |   46 +------------------
 tree.cc      |  136 ++++++++++++++++++++++++++++++++++++++++++++++++-----------
 3 files changed, 122 insertions(+), 73 deletions(-)
Index: trunk/milena/sandbox/edwin/tree/propagate.hh
===================================================================
--- trunk/milena/sandbox/edwin/tree/propagate.hh	(revision 3496)
+++ trunk/milena/sandbox/edwin/tree/propagate.hh	(revision 3497)
@@ -60,12 +60,15 @@
       void
       back_propagate_subbranch(const T& t, A& a, mln_value(A) v)
       {
-	mln_bkd_piter(T) p(t.domain());
-	for_all(p)
-	  if (t.is_a_node(p) && a(t.parent(p)) == v)
+	mln_fwd_piter(T::nodes_t) n(t.nodes());
+	for_all(n)
 	    {
-	      mln_assertion(t.is_a_node(t.parent(p)));
-	      a(p) = a(t.parent(p));
+	  mln_assertion(t.is_a_node(n));
+	  if (a(t.parent(n)) == v)
+	    {
+	      mln_assertion(t.is_a_node(t.parent(n)));
+	      a(n) = a(t.parent(n));
+	    }
 	    }
       }
     } // end of namespace mln::morpho::tree
Index: trunk/milena/sandbox/edwin/tree/tree.cc
===================================================================
--- trunk/milena/sandbox/edwin/tree/tree.cc	(revision 3496)
+++ trunk/milena/sandbox/edwin/tree/tree.cc	(revision 3497)
@@ -2,6 +2,7 @@
 #include <mln/core/image/image2d.hh>
 #include <mln/core/alias/neighb2d.hh>
 #include <mln/core/routine/duplicate.hh>
+#include <mln/core/site_set/p_set.hh>
 #include <mln/pw/all.hh>
 
 #include <mln/value/int_u8.hh>
@@ -76,6 +77,9 @@
   {
     mln_VAR(a, morpho::tree::compute_attribute_image(a_, tree_));
 
+    display_tree_attributes(tree_, a);
+    find_treshold(a, tree_);
+
     img_ = duplicate((pw::cst(lambda1) < pw::value(a) &&
 		      pw::value(a) < pw::cst(lambda2))
 		     | a.domain());
@@ -83,39 +87,125 @@
     debug::println("attribut", a);
   }
 
-//   template <typename T>
-//   inline
-//   float
-//   find_treshold(const T& t)
-//   {
-//     mln_bkd_piter(T) = p(t.domain());
+  template <typename I, typename T>
+  inline
+  float
+  find_treshold(const Image<I>& img_, const T& tree)
+  {
+    typedef p_set< mln_psite(I) > PS;
+    typedef p_array<mln_psite(I)> N;
+
+    I img = exact(img_);
+    PS pset, ps_rm; // component container.
+    mln_value(I) val, old;
+    std::vector< mln_value(I)> f_domain;
+    std::vector< unsigned> f;
+
 
+    //debug::println(img | tree.nodes());
+
+
+    N nodes = level::sort_psites_increasing(img);
+    mln_fwd_piter(N) n(nodes);
+    mln_fwd_piter(PS) p(pset), p_rm(ps_rm);
+    old = 0;
+
+    // FIXME: hack because we want iterate on nodes and we would
+    // have wanted to write:
+    //     N nodes = level::sort_psites_increasing(img | tree.nodes)
+    //
+    // but it doesn't work, so we iterate on domain regarding if n is
+    // a node.
+
+    n.start();
+    while (n.is_valid() && !tree.is_a_node(n))
+      n.next();
+
+    while (n.is_valid() && tree.is_a_node(n))
+      {
+	val = img(n);
+	do {
+	  // Remove p's children from set.
+	  // FIXME: improve deletion pass.
+	  ps_rm.clear();
+	  for_all(p)
+	    if (tree.parent(p) == n)
+	      ps_rm.insert(p);
+
+	  for_all(p_rm)
+	    pset.remove(p_rm);
+
+	  // Add the new representant to component set.
+	  pset.insert(n);
+	  do {
+	    n.next();
+	  } while (n.is_valid() && !tree.is_a_node(n));
+	} while (img(n) == val && n.is_valid());
+
+	if (pset.nsites() != old)
+	  {
+	    old = pset.nsites();
+	    f_domain.push_back(val);
+	    f.push_back(pset.nsites());
+	  }
+
+
+	  // Debug.
+	  {
+	    std::cout << " - " << val << ":" << pset.nsites() << " {";
+	    for_all(p)
+	      std::cout << p << ",";
+	    std::cout << "}" << std::endl;
+	  }
+      }
+
+    for (unsigned i = 0; i < f_domain.size() - 1; i++)
+      {
+	std::cout << "[" << f_domain[i] << ","
+		  << f_domain[i + 1] << "[ -> "
+		  << f[i] << std::endl;
+      }
+    std::cout <<  ">=" << f_domain[f_domain.size() - 1] << " -> "
+	      << f[f_domain.size() - 1] << std::endl;
+
+    return 0;
+  }
     
-//     for_all(p)
-//       if (t.is_a_node(p))
-// 	{
-// 	  if 
 
-// 	}
-//   }
 
   template <typename I, typename A>
-  void filtercheck(const Image<I>& img, const  Meta_Accumulator<A>& a)
+  void filtercheck(treefilter<I>& f, const  Meta_Accumulator<A>& a)
   {
 
     using value::label_8;
     label_8 n;
     util::array<unsigned int> counts;
+    util::array< mln_psite(I) > fnodes;
 
-    debug::println("binaire:", img);
-    mln_VAR(lbl, labeling::blobs(img, c4(), n));
-    debug::println("blob:", lbl);
+    mln_VAR(lbl, labeling::blobs(f.img(), c4(), n));
     counts = labeling::compute(a, lbl, n);
-    for (unsigned i = 0; i < counts.nelements(); i++)
+
+    mln_VAR(acc, morpho::tree::compute_attribute_image(morpho::attribute::card<I>(), f.tree()));
+    fnodes = morpho::tree::get_first_nodes(f.img(), f.tree());
+
+    mln_assertion(counts.nelements() == fnodes.nelements() + 1);
+
+    std::vector<unsigned> counts2;
+    counts2.resize(fnodes.nelements());
+    std::transform(fnodes.hook_std_vector_().begin(), fnodes.hook_std_vector_().end(),
+		   counts2.begin(), acc);
+
+    std::sort(counts.hook_std_vector_().begin(), counts.hook_std_vector_().end());
+    std::sort(counts2.begin(), counts2.end());
+
+    for (unsigned i = 0; i < counts2.size(); i++)
       {
-	std::cout << "counts[" << i << "]: " << counts[i]
+	mln_assertion(acc.domain().has(fnodes(p)));
+	std::cout << "counts[" << i << "]: (ref " << counts[i] << ") " << counts2[i]
 		  << std::endl;
       }
+
+
   }
 } // end of namespace mln
 
@@ -157,20 +247,16 @@
   else
     usage(argv);
 
+
+
   back_propagate_subbranch(f->tree(), f->img() ,true);
   back_propagate_level(f->tree(), f->img());
 
-  filtercheck(f->img(), accu::meta::count());
+  filtercheck(*f, accu::meta::count());
 
 
 
-  util::array< mln_psite_(I) > counts;
-  counts = morpho::tree::get_first_nodes(f->img(), f->tree());
-  for (unsigned i = 0; i < counts.nelements(); i++)
-    std::cout << "counts[" << i << "]: " << counts[i] << std::endl;
 
-  mln_VAR(a, morpho::tree::compute_attribute_image(morpho::attribute::card<I>(), f->tree()));
-  display_tree_attributes(f->tree(), a);
 
   io::pbm::save(f->img(), "out.pbm");
   delete f;
Index: trunk/milena/sandbox/edwin/tree/routines.hh
===================================================================
--- trunk/milena/sandbox/edwin/tree/routines.hh	(revision 3496)
+++ trunk/milena/sandbox/edwin/tree/routines.hh	(revision 3497)
@@ -49,60 +49,20 @@
 	data::fill(deja_vu, false);
 
 	bool can_break = false;
-	mln_bkd_piter(T) p(tree.domain());
+	mln_bkd_piter(T) p(tree.nodes());
 	for_all(p)
 	{
-	  if (tree.is_a_node(p) && bin(p) && bin(t.parent(p))
+	  mln_assertion(tree.is_a_node(p));
+	  if (bin(p) && !bin(tree.parent(p)))
 	    {
 	      fnodes.append(p);
 	    }
 	}
-
-	//   else if (can_break)
-// 	    {
-// 	      std::cout << p << std::endl;
-// 	      break;
-// 	    }
-
 	return fnodes;
       }
 
     }
   }
-
-//   namespace debug {
-
-//     template <typename T, typename I>
-//     void
-//     println(const T& t, const Image<I> f_)
-//     {
-//       //theo's code
-//     typedef mln_site(I) P;
-//     I f = exact(f_);
-
-//     mln_ch_value(I, bool) deja_vu;
-//     initialize(deja_vu, f);
-//     data::fill(deja_vu, false);
-
-//     typedef typename T::nodes_t nodes_t;
-//     mln_fwd_piter(T) p(t.nodes());
-//     for_all(p)
-//       {
-// 	if (deja_vu(p))
-// 	  continue;
-// 	P e = p;
-// 	do
-// 	  {
-// 	    std::cout << f(e) << ':' << e << "  ->  ";
-// 	    deja_vu(e) = true;
-// 	    e = t.parent(e);
-// 	  }
-// 	while (! deja_vu(e));
-// 	std::cout << f(e) << ':' << e << std::endl;
-//       }
-//     std::cout << std::endl;
-//     }
-//   }
 }
 
 
                    
                  
                  
                          
                            
                            1
                            
                          
                          
                            
                            0
                            
                          
                          
                            
    
                          
                        
                    
                    
                          URL: https://svn.lrde.epita.fr/svn/oln/trunk/milena/sandbox
ChangeLog:
2009-03-06  Fabien Freling  <fabien.freling(a)lrde.epita.fr>
	Add watershed implementation for IRM images.
	* fabien/bin/Makefile: Update.
	* fabien/bin/dicom2dump.cc: Update.
	* fabien/bin/dicom_mask.cc: Update.
	* fabien/igr/Makefile: Update.
	* fabien/igr/dumps: Remove.
	* fabien/igr/watershed.cc: Watershed implementation.
---
 bin/Makefile      |    3 
 bin/dicom2dump.cc |   19 ---
 bin/dicom_mask.cc |   26 ++++
 igr/Makefile      |    9 -
 igr/watershed.cc  |  320 ++++++++++++++++++++++++++++++++++++++++++++++++++++++
 5 files changed, 353 insertions(+), 24 deletions(-)
Index: trunk/milena/sandbox/fabien/igr/watershed.cc
===================================================================
--- trunk/milena/sandbox/fabien/igr/watershed.cc	(revision 0)
+++ trunk/milena/sandbox/fabien/igr/watershed.cc	(revision 3496)
@@ -0,0 +1,320 @@
+#include <iostream>
+#include <mln/core/image/image2d.hh>
+
+#include <mln/core/alias/neighb2d.hh>
+#include <mln/core/alias/window2d.hh>
+#include <mln/core/image/image_if.hh>
+
+#include <mln/io/ppm/save.hh>
+#include <mln/io/ppm/load.hh>
+#include <mln/io/pgm/load.hh>
+#include <mln/io/dicom/load.hh>
+#include <mln/io/pgm/save.hh>
+#include <mln/io/dump/save.hh>
+
+#include <mln/value/rgb8.hh>
+#include <mln/value/int_u8.hh>
+#include <mln/value/int_u12.hh>
+#include <mln/value/label_16.hh>
+
+#include <mln/level/transform.hh>
+#include <mln/level/stretch.hh>
+
+#include <mln/labeling/mean_values.hh>
+
+#include <mln/convert/to_fun.hh>
+
+#include <mln/make/graph.hh>
+
+#include <mln/morpho/gradient.hh>
+#include <mln/morpho/closing/area.hh>
+#include <mln/morpho/meyer_wst.hh>
+
+#include <mln/fun/l2l/wrap.hh>
+
+#include <mln/core/var.hh>
+#include <mln/morpho/elementary/dilation.hh>
+
+#include <mln/core/routine/extend.hh>
+
+#include <mln/util/graph.hh>
+
+#include <mln/essential/2d.hh>
+#include <mln/core/alias/vec3d.hh>
+#include <mln/debug/draw_graph.hh>
+#include <mln/util/graph.hh>
+#include <mln/accu/center.hh>
+#include <mln/io/dump/all.hh>
+#include <mln/value/label_8.hh>
+#include <mln/value/rgb16.hh>
+#include <mln/accu/compute.hh>
+#include <mln/core/alias/dpoint2d.hh>
+#include <mln/draw/box.hh>
+#include <mln/level/stretch.hh>
+#include <mln/fun/v2v/id.hh>
+#include <mln/core/image/line_graph_elt_neighborhood.hh>
+#include <mln/morpho/elementary/dilation.hh>
+#include <mln/labeling/mean_values.hh>
+#include <mln/extension/adjust_fill.hh>
+#include <mln/extract/all.hh>
+#include <mln/make/region_adjacency_graph.hh>
+
+
+
+// Given a color image and a wshed image, computes the component graph.
+// Vertex values are computed thanks to a RGB image.
+
+namespace mln
+{
+
+  template <typename V>
+  value::int_u8 dist(const V& c1, const V& c2)
+  {
+    unsigned d = math::diff_abs(c1.red(), c2.red());
+    unsigned d_;
+    d_ = math::diff_abs(c1.green(), c2.green());
+
+    if (d_ > d)
+      d = d_;
+
+    d_ = math::diff_abs(c1.blue(), c2.blue());
+
+    if (d_ > d)
+      d = d_;
+    return d;
+  }
+
+
+  // ima_v, image on graph vertices; value = mean color per vertex (watershed basin)
+  template <typename I>
+  inline
+  pw::image<fun::i2v::array<value::int_u8>,
+	    p_edges<util::graph, fun::i2v::array<mln_site(I)> > >
+  make_edge_graph_image(const I& ima_v, const util::graph& g)
+  {
+    // edge sites.
+    typedef fun::i2v::array<mln_site(I)> edge_site_t;
+    edge_site_t edge_site(g.e_nmax(), literal::origin);
+    typedef p_edges<util::graph, edge_site_t > pe_t;
+    pe_t pe(g, edge_site);
+
+    // edge values
+    typedef fun::i2v::array<value::int_u8> edge_values_t;
+    edge_values_t edge_values(g.e_nmax());
+
+    // image on graph edges
+    typedef pw::image<edge_values_t, pe_t> ima_e_t;
+    ima_e_t ima_e = (edge_values | pe);
+
+    mln_piter(ima_e_t) e(ima_e.domain());
+    for_all(e) // in ima_e
+      ima_e(e) = math::diff_abs(ima_v.function()(e.element().v1()), ima_v.function()(e.element().v2()));
+
+    return ima_e;
+  }
+
+
+  template <typename I, typename J>
+  inline
+  util::array<mln_value(I)>
+  mean_color_values(const I& input, const J& w, mln_value(J) nbasins)
+  {
+    // Cf. sandbox/theo/color/segment_rgb_pixels.cc
+
+    util::array<vec3d_f> m_3f = labeling::compute(accu::mean<mln_value(I)>(),
+						  input, // input color image
+						  w, // watershed labeling
+						  nbasins);
+    m_3f[0] = literal::zero;
+
+    util::array<mln_value(I)> m;
+    convert::from_to(m_3f, m);
+    m[0] = literal::yellow;
+
+    io::ppm::save(level::transform(w,
+	  convert::to< fun::i2v::array<mln_value(I)> >(m)),
+	"wst_rag_wshd_color.ppm");
+
+    return m;
+  }
+
+
+  template <typename I, typename J>
+  pw::image<fun::i2v::array<mln_value(I)>, p_vertices<util::graph, fun::i2v::array<mln_site(I)> > >
+  make_vertex_graph_image(const util::graph& g, const I&input, const J& w, const mln_value(J)& nbasins)
+  {
+    typedef util::array<mln_site(I)> vertex_sites_t;
+    vertex_sites_t site_values;
+    convert::from_to(labeling::compute(accu::center<mln_site(I)>(), w, nbasins),
+				       site_values);
+
+    typedef fun::i2v::array<mln_site(J)> f_sites_t;
+    f_sites_t sites;
+    convert::from_to(site_values, sites);
+
+    // p_vertices
+    typedef p_vertices<util::graph, f_sites_t> S;
+    S pv(g, sites);
+
+
+    typedef fun::i2v::array<mln_value(I)> vertex_values_t;
+    vertex_values_t vertex_values;
+    // convert::from_to(mean_color_values(input, w, nbasins), vertex_values);
+
+    mln_VAR(ima_v, (vertex_values | pv));
+
+    return ima_v;
+  }
+
+
+  template <typename I, typename V>
+  struct edge_to_color : Function_p2v< edge_to_color<I,V> >
+  {
+    typedef V result;
+
+    edge_to_color(const I& ima)
+      : ima_(ima)
+    {
+    }
+
+    V
+    operator()(const unsigned& e) const
+    {
+      return convert::to<V>(ima_.function()(e));
+    }
+
+    const I& ima_;
+  };
+
+  template <typename V>
+  inline
+  unsigned
+  find_root(util::array<V>& parent, const unsigned& x)
+  {
+    if (parent[x] == x)
+      return x;
+    else
+      return parent[x] = find_root(parent, parent[x]);
+  }
+
+
+  template <typename I, typename V, typename E>
+  inline
+  image2d<mln_value(I)>
+  make_debug_graph_image(const I& input,
+			 const V& ima_v, const E& ima_e,
+			 unsigned box_size, const value::rgb8& bg)
+  {
+    image2d<mln_value(I)> ima;
+    initialize(ima, input);
+
+    data::fill(ima, bg);
+    debug::draw_graph(ima, ima_v.domain(),
+		      pw::cst(mln_value(I)(literal::green)),
+		      edge_to_color<E, mln_value(I)>(ima_e));
+
+    dpoint2d tl(-box_size,-box_size);
+    dpoint2d br(box_size,box_size);
+    mln_piter(V) p(ima_v.domain());
+    for_all(p)
+    {
+      box2d b(p + tl, p + br);
+      b.crop_wrt(ima.domain());
+      data::fill((ima | b).rw(), convert::to<mln_value(I)>(ima_v(p)));
+    }
+
+    return ima;
+  }
+
+}
+
+
+///////////////////
+//               //
+// Main Function //
+//               //
+///////////////////
+
+
+int main(int argc, char *argv[])
+{
+  using namespace mln;
+  using value::int_u8;
+  using value::int_u12;
+  using value::rgb8;
+  using value::label_16;
+
+  if (argc < 4)
+  {
+    std::cout << "Usage: " << argv[0] << " <ima.dcm> <closure_lambda> <box_size>"
+	      << std::endl;
+    return 1;
+  }
+
+  unsigned box_size = atoi(argv[3]);
+
+  image2d<int_u12> dcm;
+  io::dicom::load(dcm, argv[1]);
+
+  image2d<int_u12> grad = morpho::gradient(dcm, win_c4p());
+  image2d<int_u12> clo = morpho::closing::area(grad, c4(), atoi(argv[2]));
+
+  label_16 nbasins;
+  image2d<label_16> wshed = morpho::meyer_wst(clo, c4(), nbasins);
+
+  mln_VAR(vol2_, morpho::elementary::dilation(extend(wshed | (pw::value(wshed) == 0u), wshed), c8()));
+
+  data::fill((wshed | (pw::value(wshed) == 0u)).rw(), vol2_);
+
+  /// Build graph
+  util::graph g = make::graph(wshed, c4(), nbasins);
+  // Build graph images and compute distance values with a RGB image.
+  mln_VAR(ima_v, make_vertex_graph_image(g, dcm, wshed, nbasins));
+  mln_VAR(ima_e, make_edge_graph_image(ima_v, g));
+
+  /// Try to merge vertices.
+  mln_piter_(ima_e_t) e(ima_e.domain());
+  util::array<label_16> parent(g.v_nmax());
+  for (unsigned i = 0; i < parent.nelements(); ++i)
+    parent[i] = i;
+
+  for_all(e)
+  {
+    unsigned v1 = e.element().v1();
+    unsigned v2 = e.element().v2();
+    if (ima_e(e) <= (unsigned)(atoi(argv[5]))
+	&& find_root(parent, v1) != find_root(parent, v2))
+      parent[find_root(parent, v1)] = find_root(parent, v2);
+  }
+
+  fun::i2v::array<label_16> f(parent.nelements());
+  std::vector<unsigned> new_label(parent.nelements(), 0);
+  unsigned nbasins2 = 0;
+  for (unsigned i = 0; i < parent.nelements(); ++i)
+  {
+    unsigned p = find_root(parent, i);
+    mln_assertion(parent[p] == find_root(parent, i));
+    if (new_label[p] == 0)
+      new_label[p] = nbasins2++;
+    f(i) = new_label[p];
+  }
+  mln_invariant(f(0) == 0u);
+  --nbasins2; // nbasins2 does not count the basin with label 0.
+  image2d<label_16> wsd2 = level::transform(wshed, f);
+
+
+  /// Reconstruct a graph from the simplified image.
+  util::graph g2 = make::graph(wsd2, c4(), nbasins2);
+
+  // Compute distance values with a RGB image.
+  mln_VAR(ima_v2, make_vertex_graph_image(g2, dcm, wsd2, nbasins2));
+  mln_VAR(ima_e2, make_edge_graph_image(ima_v2, g2));
+
+  mln_VAR(wsd2_, morpho::elementary::dilation(extend(wsd2 | (pw::value(wsd2) == 0u), wsd2), c8()));
+
+  data::fill((wsd2 | (pw::value(wsd2) == 0u)).rw(), wsd2_);
+
+  //io::ppm::save(labeling::mean_values(dcm, wsd2, nbasins2), "wst_rag_mean_colors.ppm");
+  //io::ppm::save(make_debug_graph_image(dcm, ima_v2, ima_e2, box_size, literal::white), "wst_rag_graph_image2_white.ppm");
+  //io::ppm::save(make_debug_graph_image(dcm, ima_v2, ima_e2, box_size, literal::black), "wst_rag_graph_image2_black.ppm");
+}
Index: trunk/milena/sandbox/fabien/igr/Makefile
===================================================================
--- trunk/milena/sandbox/fabien/igr/Makefile	(revision 3495)
+++ trunk/milena/sandbox/fabien/igr/Makefile	(revision 3496)
@@ -8,7 +8,7 @@
 	    -framework CoreFoundation
 CXXFLAGS = -DNDEBUG -O1
 
-all: 2d 3d
+all: 2d 3d wsd
 
 2d: seg_vol_irm.hh seg2d.cc
 	g++ -I../../../ ${DICOM_INC} ${DICOM_LIB} ${CXXFLAGS} seg2d.cc -o seg2d
@@ -16,11 +16,8 @@
 3d: seg_vol_irm.hh seg3d.cc
 	g++ -I../../../ ${DICOM_INC} ${DICOM_LIB} ${CXXFLAGS} seg3d.cc -o seg3d
 
-grad: grad_clo_and_wshd.cc
-	g++ -I../../../ ${DICOM_INC} ${DICOM_LIB} ${CXXFLAGS} $^ -o grad_clo
-
-wst: wst_rag.cc
-	g++ -I../../../ ${DICOM_INC} ${DICOM_LIB} ${CXXFLAGS} $^ -o wst_rag
+wsd: watershed.cc
+	g++ -I../../../ ${DICOM_INC} ${DICOM_LIB} ${CXXFLAGS} $^ -o wsd
 
 clean:
 	rm -rf *.dump *.p?m *.plot *.log *.csv
Index: trunk/milena/sandbox/fabien/bin/dicom2dump.cc
===================================================================
--- trunk/milena/sandbox/fabien/bin/dicom2dump.cc	(revision 3495)
+++ trunk/milena/sandbox/fabien/bin/dicom2dump.cc	(revision 3496)
@@ -2,7 +2,6 @@
 #include <mln/core/image/image2d.hh>
 #include <mln/core/image/image3d.hh>
 
-#include <mln/value/int_u8.hh>
 #include <mln/value/int_u12.hh>
 #include <mln/io/dicom/load.hh>
 #include <mln/io/dump/save.hh>
@@ -17,31 +16,15 @@
 }
 
 
-/*int main(int argc, char* argv[])
-{
-  using namespace mln;
-  using value::int_u12;
-
-  if (argc != 3)
-    return usage(argv);
-
-  image3d<int_u12> ima;
-  io::dicom::load(ima, argv[1]);
-  io::dump::save(ima, argv[2]);
-
-  return 0;
-}*/
-
 int main(int argc, char* argv[])
 {
   using namespace mln;
-  using value::int_u8;
   using value::int_u12;
 
   if (argc != 3)
     return usage(argv);
 
-  image2d<int_u8> ima;
+  image3d<int_u12> ima;
   io::dicom::load(ima, argv[1]);
   io::dump::save(ima, argv[2]);
 
Index: trunk/milena/sandbox/fabien/bin/dicom_mask.cc
===================================================================
--- trunk/milena/sandbox/fabien/bin/dicom_mask.cc	(revision 3495)
+++ trunk/milena/sandbox/fabien/bin/dicom_mask.cc	(revision 3496)
@@ -1,6 +1,9 @@
 #include <mln/core/concept/image.hh>
 #include <mln/core/image/image2d.hh>
 
+#include <mln/geom/ncols.hh>
+#include <mln/geom/nrows.hh>
+
 #include <mln/value/int_u8.hh>
 #include <mln/io/dicom/load.hh>
 #include <mln/io/pbm/save.hh>
@@ -33,6 +36,29 @@
   image2d<int_u8> input;
   io::dicom::load(input, argv[1]);
 
+  util::array<unsigned> xproj(geom::nrows(input), 0);
+  util::array<unsigned> yproj(geom::ncols(input), 0);
+
+  mln_piter_(image2d<int_u8>) p(input.domain());
+  for_all(p)
+  {
+    xproj[p.row()] += input(p);
+    yproj[p.col()] += input(p);
+  }
+
+  // Plot files
+  std::ofstream fout_x("x.plot");
+  for (unsigned int i = 0; i < xproj.nelements(); ++i)
+  {
+    fout_x << i << " " << xproj[i] << std::endl;
+  }
+
+  std::ofstream fout_y("y.plot");
+  for (unsigned int i = 0; i < yproj.nelements(); ++i)
+  {
+    fout_y << i << " " << yproj[i] << std::endl;
+  }
+
   image2d<bool> ima = level::transform(input, fun::v2b::threshold<int_u8>(1));
   io::pbm::save(ima, argv[2]);
 }
Index: trunk/milena/sandbox/fabien/bin/Makefile
===================================================================
--- trunk/milena/sandbox/fabien/bin/Makefile	(revision 3495)
+++ trunk/milena/sandbox/fabien/bin/Makefile	(revision 3496)
@@ -21,3 +21,6 @@
 
 dump: dicom2dump.cc
 	g++ -I../../../ ${DICOM_INC} ${DICOM_LIB} ${CXXFLAGS} $^ -o dicom2dump
+
+pgm: dicom2pgm.cc
+	g++ -I../../../ ${DICOM_INC} ${DICOM_LIB} ${CXXFLAGS} $^ -o dicom2pgm
                    
                  
                  
                          
                            
                            1
                            
                          
                          
                            
                            0
                            
                          
                          
                            
    
                          
                        
                    
                    
                        https://svn.lrde.epita.fr/svn/oln/trunk/milena
Index: ChangeLog
from  Thierry Geraud  <thierry.geraud(a)lrde.epita.fr>
	Have all labeling routines be consistent.
	* mln/core/concept/window.hh,
	* mln/core/concept/neighborhood.hh
	(positive_offsets_wrt): New.
	* mln/morpho/tree/compute_parent.hh (todo): New.
	(doc): Augment.
	* mln/morpho/tree/data.hh (todo): New.
	* mln/canvas/labeling.hh: Swap fwd and bkd between both passes.
	(todo): New.
	Now it is consistent with labeling::blobs.
	* tests/level/sort_psites.cc: Test reversibility.
	* tests/morpho/tree/data.cc: Precise fwd when needed.
	* tests/labeling/foreground.cc: Upgrade doc style.
	Augment test.
 mln/canvas/labeling.hh            |   26 +++++-------
 mln/core/concept/neighborhood.hh  |   18 ++++++++
 mln/core/concept/window.hh        |   30 ++++++++++++++
 mln/morpho/tree/compute_parent.hh |   78 ++++++++++++++++++++++++++++++++++----
 mln/morpho/tree/data.hh           |    7 ++-
 tests/labeling/foreground.cc      |   38 +++++++++++++++---
 tests/level/sort_psites.cc        |   19 ++++++---
 tests/morpho/tree/data.cc         |   15 +++++--
 8 files changed, 193 insertions(+), 38 deletions(-)
Index: mln/core/concept/window.hh
--- mln/core/concept/window.hh	(revision 3494)
+++ mln/core/concept/window.hh	(working copy)
@@ -121,6 +121,10 @@
   
   template <typename I, typename W>
   util::array<int>
+  positive_offsets_wrt(const Image<I>& ima, const Window<W>& win);
+  
+  template <typename I, typename W>
+  util::array<int>
   negative_offsets_wrt(const Image<I>& ima, const Window<W>& win);
   
 
@@ -346,6 +350,32 @@
   template <typename I, typename W>
   inline
   util::array<int>
+  positive_offsets_wrt(const Image<I>& ima_, const Window<W>& win_)
+  {
+    mln_is_simple_window(W)::check();
+
+    const I& ima = exact(ima_);
+    const W& win = exact(win_);
+    mln_precondition(ima.is_valid());
+    mln_precondition(win.is_valid());
+
+    util::array<int> arr;
+    unsigned n = win.size();
+    
+    for (unsigned i = 0; i < n; ++i)
+      {
+	int offset = ima.delta_index(win.dp(i));
+	if (offset > 0)
+	  arr.append(offset);
+      }
+
+    return arr;
+  }
+
+
+  template <typename I, typename W>
+  inline
+  util::array<int>
   negative_offsets_wrt(const Image<I>& ima_, const Window<W>& win_)
   {
     mln_is_simple_window(W)::check();
Index: mln/core/concept/neighborhood.hh
--- mln/core/concept/neighborhood.hh	(revision 3494)
+++ mln/core/concept/neighborhood.hh	(working copy)
@@ -104,6 +104,10 @@
 
   template <typename I, typename N>
   util::array<int>
+  positive_offsets_wrt(const Image<I>& ima, const Neighborhood<N>& nbh);
+
+  template <typename I, typename N>
+  util::array<int>
   negative_offsets_wrt(const Image<I>& ima, const Neighborhood<N>& nbh);
 
 
@@ -166,6 +170,20 @@
 
   template <typename I, typename N>
   util::array<int>
+  positive_offsets_wrt(const Image<I>& ima_, const Neighborhood<N>& nbh_)
+  {
+    mln_is_simple_neighborhood(N)::check();
+
+    const I& ima = exact(ima_);
+    const N& nbh = exact(nbh_);
+    mln_precondition(ima.is_valid());
+    mln_precondition(nbh.is_valid());
+
+    return positive_offsets_wrt(ima, nbh.win());
+  }
+
+  template <typename I, typename N>
+  util::array<int>
   negative_offsets_wrt(const Image<I>& ima_, const Neighborhood<N>& nbh_)
   {
     mln_is_simple_neighborhood(N)::check();
Index: mln/morpho/tree/compute_parent.hh
--- mln/morpho/tree/compute_parent.hh	(revision 3494)
+++ mln/morpho/tree/compute_parent.hh	(working copy)
@@ -1,4 +1,5 @@
-// Copyright (C) 2008 EPITA Research and Development Laboratory (LRDE)
+// Copyright (C) 2008, 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
@@ -33,6 +34,11 @@
 /// Compute a canonized tree from an image.
 ///
 /// \todo Specialize for low quant (and try fastest).
+///
+/// \todo Augment and improve documentation.
+///
+/// \todo Change level::sort so that the explanations below are valid
+/// whatever the choice 'increasing or decreasing'.
 
 # include <mln/core/concept/image.hh>
 # include <mln/core/concept/neighborhood.hh>
@@ -55,16 +61,72 @@
       /// "natural" childhood relationship.  The parenthood is thus
       /// inverted w.r.t. to \p s.
       ///
-      /// It is very convenient since all processing upon the parent
-      /// tree are performed following \p s (in the default "forward"
-      /// way).
+      /// It is very convenient since most processing routines upon
+      /// the parent tree are performed following \p s (in the default
+      /// "forward" way).  Indeed that is the way to propagate
+      /// information from parents to children.
       ///
-      /// FIXME: Put it more clearly...
       ///
       /// The parent result image verifies: \n
       /// - p is root iff parent(p) == p \n
       /// - p is a node iff either p is root or f(parent(p)) != f(p).
-
+      ///
+      ///
+      ///
+      /// The choice "s means childhood" is consistent with labeling
+      /// in binary images.  In that particular case, while browsing
+      /// the image in forward scan (video), we expect to find first a
+      /// tree root (a first point, representative of a component) and
+      /// then the other component points.  Please note that it leads
+      /// to increasing values of labels in the "natural" video scan.
+      ///
+      /// Since mathematical morphology on functions is related to
+      /// morphology on sets, we clearly want to keep the equivalence
+      /// between "component labeling" and "component filtering" using
+      /// trees.
+      ///
+      ///
+      /// FIXME: Put it more clearly...  Insert pictures!
+      ///
+      /// A binary image:
+      /// 
+      /// - | | - - \n
+      /// - | | - | \n
+      /// - - - - - \n
+      /// - - | | - \n
+      ///
+      /// where '|' means true and '-' means false.
+      ///
+      /// Its labeling:
+      ///
+      /// 0 1 1 0 0 \n
+      /// 0 1 1 0 2 \n
+      /// 0 0 0 0 0 \n
+      /// 0 0 3 3 0 \n
+      ///
+      /// The corresponding forest:
+      ///
+      /// x o . x x \n
+      /// x . . x o \n
+      /// x x x x x \n
+      /// x x o . x \n
+      ///
+      /// where 'x' means "no data", 'o' is a tree root
+      /// (representative point for a component), and '.' is a tree
+      /// regular (non-root) point (in a component by not its
+      /// representative point).
+      ///
+      ///
+      /// The forest, with the parent relationship looks like:
+      ///
+      ///  o < .				\n
+      ///  ^ r					\n
+      ///  .   .       o			\n
+      ///					\n
+      ///					\n
+      ///      o < .				\n
+      ///
+      ///
       template <typename I, typename N, typename S>
       mln_ch_value(I, mln_psite(I))
       compute_parent(const Image<I>& f, const Neighborhood<N>& nbh,
@@ -159,7 +221,7 @@
 	    data::fill(deja_vu, false);
 
 	    // Body.
-	    mln_bkd_piter(S) p(s);
+	    mln_bkd_piter(S) p(s); // Backward.
 	    mln_niter(N) n(nbh, p);
 	    for_all(p)
 	    {
@@ -183,7 +245,7 @@
 
 	    // Canonization.
 	    {
-	      mln_fwd_piter(S) p(s);
+	      mln_fwd_piter(S) p(s); // Forward.
 	      for_all(p)
 	      {
 		P q = parent(p);
Index: mln/morpho/tree/data.hh
--- mln/morpho/tree/data.hh	(revision 3494)
+++ mln/morpho/tree/data.hh	(working copy)
@@ -1,4 +1,5 @@
-// Copyright (C) 2008 EPITA Research and Development Laboratory (LRDE)
+// Copyright (C) 2008, 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
@@ -31,6 +32,10 @@
 /// \file mln/morpho/tree/data.hh
 ///
 /// FIXME: First Attempt.
+///
+/// \todo Fix the issue pointed to by Edwin without modifying the way
+/// sites are browsed (see the documentation of compute_parent to
+/// learn why we want the 1st pass to be in forward scan of s).
 
 # include <mln/morpho/tree/compute_parent.hh>
 # include <mln/core/image/sub_image.hh>
Index: mln/canvas/labeling.hh
--- mln/canvas/labeling.hh	(revision 3494)
+++ mln/canvas/labeling.hh	(working copy)
@@ -1,5 +1,5 @@
-// Copyright (C) 2007, 2008, 2009 EPITA Research and Development Laboratory
-// (LRDE)
+// Copyright (C) 2007, 2008, 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
@@ -33,8 +33,8 @@
 ///
 /// Connected component labeling of the object part in a binary image.
 ///
-/// \todo Make the fastest version work.
-/// FIXME: is 'status' useful?
+/// \todo Can we get rid of 'deja_vu' (while playing with the border)
+/// in the fastest video version?
 
 # include <mln/core/concept/image.hh>
 # include <mln/data/fill.hh>
@@ -139,7 +139,7 @@
 
 	  // Output.
 	  mln_ch_value(I, L) output;
-	  bool status;
+	  bool status; // FIXME: Is-it useful?
 
 	  // Initialization.
 	  {
@@ -157,7 +157,7 @@
 
 	  // First Pass.
 	  {
-	    mln_fwd_piter(S) p(s);
+	    mln_bkd_piter(S) p(s);  // Backward.
 	    mln_niter(N) n(nbh, p);
 	    for_all(p) if (f.handles(p))
 	      {
@@ -187,7 +187,7 @@
 
 	  // Second Pass.
 	  {
-	    mln_bkd_piter(S) p(s);
+	    mln_fwd_piter(S) p(s); // Forward.
 	    for_all(p) if (f.handles(p))
 	      {
 		if (parent(p) == p) // if p is root
@@ -274,10 +274,10 @@
 
 	// First Pass.
 	{
-	  util::array<int> dp = negative_offsets_wrt(input, nbh);
+	  util::array<int> dp = positive_offsets_wrt(input, nbh);
 	  const unsigned n_nbhs = dp.nelements();
 
-	  mln_pixter(const I) px(input);
+	  mln_bkd_pixter(const I) px(input); // Backward.
 	  for_all(px)
 	  {
 	    unsigned p = px.offset();
@@ -311,7 +311,7 @@
 
 	// Second Pass.
 	{
-	  mln_bkd_pixter(const I) px(input);
+	  mln_fwd_pixter(const I) px(input); // Forward.
 	  for_all(px)
 	  {
 	    unsigned p = px.offset();
@@ -394,8 +394,7 @@
 
 	// First Pass.
 	{
-
-	  for (unsigned i = 0; i < n_points; ++i)
+	  for (int i = n_points - 1; i >=0; --i) // Backward.
 	    {
 	      unsigned p = s[i];
 	      if (! f.handles_(p))
@@ -425,13 +424,12 @@
 		    f.do_no_union_(n, p);
 		}
 	      deja_vu.element(p) = true;
-
 	    }
 	}
 
 	// Second Pass.
 	{
-	  for (int i = n_points - 1; i >=0; --i)
+ 	  for (unsigned i = 0; i < n_points; ++i) // Forward.
 	    {
 	      unsigned p = s[i];
 	      if (! f.handles_(p))
Index: tests/level/sort_psites.cc
--- tests/level/sort_psites.cc	(revision 3494)
+++ tests/level/sort_psites.cc	(working copy)
@@ -1,5 +1,5 @@
-// Copyright (C) 2007, 2008 EPITA Research and Development Laboratory
-// (LRDE)
+// Copyright (C) 2007, 2008, 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
@@ -31,7 +31,7 @@
 /// Tests on mln::level::sort_psites.
 
 #include <mln/core/image/image2d.hh>
-#include <mln/debug/iota.hh>
+#include <mln/make/image2d.hh>
 #include <mln/level/sort_psites.hh>
 #include <mln/core/site_set/p_array.hh>
 
@@ -40,11 +40,20 @@
 {
   using namespace mln;
 
-  image2d<int> ima(3, 3);
-  debug::iota (ima);
+  int vals[] = { 0, 3, 4,
+		 2, 2, 2,
+		 0, 1, 4 };
+  image2d<int> ima = make::image2d(vals);
   p_array<point2d> array_inc = level::sort_psites_increasing(ima);
   p_array<point2d> array_dec = level::sort_psites_decreasing(ima);
 
+  {
+    p_array<point2d>::fwd_piter p1(array_inc);
+    p_array<point2d>::bkd_piter p2(array_dec);
+    for_all_2(p1, p2)
+      mln_assertion(ima(p1) == ima(p2));
+  }
+
   p_array<point2d> array_inc_ref;
   p_array<point2d> array_dec_ref;
 
Index: tests/morpho/tree/data.cc
--- tests/morpho/tree/data.cc	(revision 3494)
+++ tests/morpho/tree/data.cc	(working copy)
@@ -1,4 +1,5 @@
-// Copyright (C) 2008 EPITA Research and Development Laboratory (LRDE)
+// Copyright (C) 2008, 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
@@ -62,9 +63,17 @@
 
   {
     std::cout << "nodes = ";
-    tree_t::nodes_t::piter p(t.nodes());
+    tree_t::nodes_t::fwd_piter p(t.nodes());
     for_all(p)
       std::cout << p << ' ';
+    std::cout << std::endl;
+  }
+  {
+    std::cout << "nodes = ";
+    tree_t::fwd_piter p(t.domain());
+    for_all(p)
+      if (t.is_a_node(p))
+	std::cout << p << ' ';
     std::cout << std::endl
 	      << std::endl;
   }
@@ -73,7 +82,7 @@
   {
     image2d<unsigned> area(ima.domain());
     data::fill(area, 1);
-    tree_t::piter p(t.domain());
+    tree_t::fwd_piter p(t.domain());
     for_all(p)
       if (! t.is_root(p))
 	area(t.parent(p)) += area(p);
Index: tests/labeling/foreground.cc
--- tests/labeling/foreground.cc	(revision 3494)
+++ tests/labeling/foreground.cc	(working copy)
@@ -1,4 +1,5 @@
-// Copyright (C) 2007, 2008 EPITA Research and Development Laboratory (LRDE)
+// Copyright (C) 2007, 2008, 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
@@ -25,14 +26,15 @@
 // reasons why the executable file might be covered by the GNU General
 // Public License.
 
-/*! \file tests/labeling/foreground.cc
- *
- * \brief Test on mln::labeling::foreground.
- */
+/// \file tests/labeling/foreground.cc
+///
+/// Test on mln::labeling::foreground.
 
 #include <mln/core/image/image2d.hh>
+#include <mln/core/var.hh>
 #include <mln/io/pbm/load.hh>
 #include <mln/core/alias/neighb2d.hh>
+#include <mln/level/compare.hh>
 #include <mln/labeling/foreground.hh>
 
 #include "tests/data.hh"
@@ -42,8 +44,30 @@
 {
   using namespace mln;
 
-  image2d<bool> pic = io::pbm::load(MLN_IMG_DIR "/picasso.pbm");
+  typedef image2d<bool> I;
+  mln_VAR(nbh, c4());
+
+  I pic = io::pbm::load(MLN_IMG_DIR "/picasso.pbm");
+  image2d<unsigned> out, ref;
+
   unsigned n;
-  labeling::foreground(pic, c4(), n);
+  out = labeling::foreground(pic, nbh, n); // Calls the fastest 'video'
+					   // version.
   mln_assertion(n == 33);
+
+  {
+    // Note that  labeling::foreground  actually is  labeling::level
+    // which calls  canvas::labeling_video  and its generic dispatch
+    // leads to  canvas::impl::generic::labeling.
+
+    labeling::impl::level_functor<I> f(pic, true);
+
+    unsigned n_;
+    ref = canvas::impl::generic::labeling(pic, nbh, n_,
+					  pic.domain(),
+					  f);
+    mln_invariant(n_ == n);
+    mln_invariant(ref == out);
+  }
+
 }
                    
                  
                  
                          
                            
                            1
                            
                          
                          
                            
                            0