Milena (Olena)  User documentation 2.0a Id
 All Classes Namespaces Functions Variables Typedefs Enumerator Groups Pages
artificial_line_graph_image_wst.cc
1 // Copyright (C) 2008, 2009, 2010 EPITA Research and Development
2 // Laboratory (LRDE)
3 //
4 // This file is part of Olena.
5 //
6 // Olena is free software: you can redistribute it and/or modify it under
7 // the terms of the GNU General Public License as published by the Free
8 // Software Foundation, version 2 of the License.
9 //
10 // Olena is distributed in the hope that it will be useful,
11 // but WITHOUT ANY WARRANTY; without even the implied warranty of
12 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
13 // General Public License for more details.
14 //
15 // You should have received a copy of the GNU General Public License
16 // along with Olena. If not, see <http://www.gnu.org/licenses/>.
17 //
18 // As a special exception, you may use this file as part of a free
19 // software project without restriction. Specifically, if other files
20 // instantiate templates or use macros or inline functions from this
21 // file, or you compile this file and link it with other files to produce
22 // an executable, this file does not by itself cause the resulting
23 // executable to be covered by the GNU General Public License. This
24 // exception does not however invalidate any other reasons why the
25 // executable file might be covered by the GNU General Public License.
26 
27 /* FIXME: We should factor as much things as possible between
28  tests/morpho/lena_line_graph_image_wst1.cc,
29  tests/morpho/lena_line_graph_image_wst2.cc and this file, starting
30  with conversion routines. */
31 
49 #include <mln/core/image/image2d.hh>
50 #include <mln/core/alias/point2d.hh>
51 #include <mln/core/alias/window2d.hh>
52 #include <mln/core/alias/neighb2d.hh>
53 
55 #include <mln/core/image/edge_image.hh>
56 #include <mln/pw/all.hh>
57 #include <mln/fun/i2v/array.hh>
58 #include <mln/util/graph.hh>
59 
60 #include <mln/morpho/line_gradient.hh>
61 #include <mln/morpho/closing/area_on_vertices.hh>
62 #include <mln/morpho/meyer_wst.hh>
63 #include <mln/data/stretch.hh>
64 
65 #include <mln/value/int_u8.hh>
66 #include <mln/value/int_u16.hh>
67 #include <mln/value/rgb8.hh>
68 #include <mln/literal/black.hh>
69 #include <mln/literal/colors.hh>
70 
71 #include <mln/io/pgm/load.hh>
72 #include <mln/io/pgm/save.hh>
73 #include <mln/io/ppm/save.hh>
74 
75 #include <mln/math/max.hh>
76 #include <mln/math/abs.hh>
77 
78 #include <mln/opt/at.hh>
79 
80 #include "tests/data.hh"
81 
82 
83 
84 int main()
85 {
86  using namespace mln;
87  using value::int_u8;
88  using value::int_u16;
89  using value::rgb8;
90 
91  /*--------.
92  | Input. |
93  `--------*/
94 
95  // Dimensions.
96  const unsigned nrows = 100;
97  const unsigned ncols = 100;
98  const unsigned square_length = 3;
99  typedef int_u8 input_val_t;
100  // Create a checkboard image.
101  image2d<input_val_t> input (nrows, ncols);
102  for (unsigned r = 0; r < nrows; ++r)
103  for (unsigned c = 0; c < ncols; ++c)
104  opt::at(input, r,c) =
105  ((r / square_length) % 2 == (c / square_length) % 2)
106  ? mln_min(input_val_t)
107  : mln_max(input_val_t);
108  mln_assertion((nrows * ncols) == 10000);
109  mln_assertion((2 * nrows * ncols - (nrows + ncols)) == 19800);
110 
111  /*----------------.
112  | Line gradient. |
113  `----------------*/
114 
115  // Line graph image.
116  typedef edge_image<util::site_pair<point2d>, input_val_t, util::graph> lg_ima_t;
117  lg_ima_t lg_ima = morpho::line_gradient(input);
118 
119  /*------.
120  | WST. |
121  `------*/
122 
123  typedef lg_ima_t::nbh_t nbh_t;
124  nbh_t nbh;
125 
126  // Perform a Watershed Transform.
127  typedef edge_image<util::site_pair<point2d>, unsigned, util::graph> wshed_t;
128  unsigned nbasins;
129  wshed_t wshed = morpho::meyer_wst(lg_ima, nbh, nbasins);
130  mln_assertion(nbasins == 1155);
131 
132  /*---------.
133  | Output. |
134  `---------*/
135 
136  // FIXME: Inlined conversion, to be reifed into a routine.
137 
138  // Save the result in gray levels (data) + color (wshed).
139 
140  // Data.
141  typedef rgb8 output_val_t;
142  typedef image2d<output_val_t> output_t;
143  point2d output_pmin = input.domain().pmin();
144  point2d output_pmax(input.domain().pmax()[0] * 2,
145  input.domain().pmax()[1] * 2);
146  output_t output(box2d(output_pmin, output_pmax));
147  data::fill(output, literal::black);
148  mln_fwd_piter_(image2d<input_val_t>) p(input.domain());
149  for_all(p)
150  {
151  // Equivalent of P in OUTPUT.
152  point2d q(p[0] * 2, p[1] * 2);
153  input_val_t v = input(p);
154  /* FIXME: Use a conversion function from input_val_t to
155  output_val_t instead of an explicit construction. */
156  output(q) = output_val_t(v, v, v);
157  }
158  // Interpolate missing points in OUTPUT.
159  mln_piter_(output_t) p_out(output.domain());
160  for_all(p_out)
161  {
162  // Process points on even rows and odd columns
163  if (p_out[0] % 2 == 0 && p_out[1] % 2 == 1)
164  output(p_out) = (output(p_out + left) + output(p_out + right)) / 2;
165  // Process points on odd rows and even columns
166  if (p_out[0] % 2 == 1 && p_out[1] % 2 == 0)
167  output(p_out) = (output(p_out + up) + output(p_out + down)) / 2;
168  // Process points on odd rows and odd columns
169  if (p_out[0] % 2 == 1 && p_out[1] % 2 == 1)
170  output(p_out) =
171  (output(p_out + dpoint2d(-1, -1)) +
172  output(p_out + dpoint2d(-1, +1)) +
173  output(p_out + dpoint2d(+1, -1)) +
174  output(p_out + dpoint2d(+1, +1))) / 4;
175  }
176  // Draw the watershed.
177  /* FIXME: We should draw the watershed on another image and
178  superimpose it on OUTPUT instead of drawing it directly into
179  OUTPUT. */
180  mln_piter_(wshed_t) pw(wshed.domain());
181  for_all(pw)
182  {
183  if (wshed(pw) == 0)
184  {
185  mln_psite_(lg_ima_t) pp(pw);
186  // Equivalent of the line (edge) PP in OUTPUT.
187  int row1 = pp.first()[0] * 2;
188  int col1 = pp.first()[1] * 2;
189  int row2 = pp.second()[0] * 2;
190  int col2 = pp.second()[1] * 2;
191  point2d q((row1 + row2) / 2, (col1 + col2) / 2);
192  // Print the watershed in red.
193  output(q) = literal::red;
194  }
195  }
196  // Fill the holes, so that the watershed looks connected.
197  /* FIXME: This approach is bad: it creates thick lines of watershed.
198  We should probably solve this when we ``paint'' the watershed
199  over the ``doubled'' image.
200 
201  A better approach is probably to iterate over the set of vertices,
202  and ``connect'' edges according to patterns (vertically or
203  horizontally connected egdes member of the watershed, etc.). */
204  // Reuse the piter on OUTPUT.
205  for_all (p_out)
206  // Only handle points on odd rows and columns.
207  if (p_out[0] % 2 == 1 && p_out[1] % 2 == 1)
208  {
209  // Count the number of adjacent watershed points. If there are
210  // two or more, consider, create a watershed point.
211  /* FIXME: Iterating over a c4 window would be more elegant, of
212  course. */
213  unsigned nwsheds =
214  (output.has(p_out + up ) && output(p_out + up ) == literal::red) +
215  (output.has(p_out + down ) && output(p_out + down ) == literal::red) +
216  (output.has(p_out + left ) && output(p_out + right) == literal::red) +
217  (output.has(p_out + right) && output(p_out + left ) == literal::red);
218  if (nwsheds >= 2)
219  output(p_out) = literal::red;
220  }
221  io::ppm::save(output, "artificial_line_graph_image_wst-out.ppm");
222 }