Milena (Olena)  User documentation 2.0a Id
 All Classes Namespaces Functions Variables Typedefs Enumerator Groups Pages
lena_line_graph_image_wst1.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 and
29  tests/morpho/lena_line_graph_image_wst2.cc, starting from conversion
30  routines. */
31 
48 #include <map>
49 #include <vector>
50 
51 #include <mln/util/ord.hh>
52 
53 #include <mln/core/image/image2d.hh>
54 #include <mln/core/alias/point2d.hh>
55 #include <mln/core/alias/window2d.hh>
56 #include <mln/core/alias/neighb2d.hh>
57 
58 #include <mln/convert/to_window.hh>
59 
61 #include <mln/core/image/edge_image.hh>
62 #include <mln/core/var.hh>
63 #include <mln/fun/i2v/array.hh>
64 #include <mln/util/graph.hh>
65 
66 #include <mln/morpho/gradient.hh>
67 #include <mln/morpho/closing/area.hh>
68 #include <mln/morpho/meyer_wst.hh>
69 
70 #include <mln/value/int_u8.hh>
71 #include <mln/value/rgb8.hh>
72 #include <mln/literal/black.hh>
73 #include <mln/literal/colors.hh>
74 
75 #include <mln/io/pgm/load.hh>
76 #include <mln/io/ppm/save.hh>
77 
78 #include <mln/math/max.hh>
79 #include <mln/math/abs.hh>
80 
81 #include <mln/util/site_pair.hh>
82 
83 #include "tests/data.hh"
84 
85 
86 
87 int main()
88 {
89  using namespace mln;
90  using value::int_u8;
91  using value::rgb8;
92 
93  /*--------.
94  | Input. |
95  `--------*/
96 
97  typedef int_u8 input_val_t;
99  io::pgm::load(input, MLN_IMG_DIR "/tiny.pgm");
100 
101  // In this test, the gradient is directly computed on the input
102  // image, not on the edges of the line graph image.
103  image2d<input_val_t> gradient =
105 
106  // Simplify the input image.
107  image2d<input_val_t> work(input.domain());
108  work = morpho::closing::area(gradient, c4(), 10);
109 
110  /*-------------.
111  | Line graph. |
112  `-------------*/
113 
114  // FIXME: Inlined conversion, to be reifed into a routine.
115 
116  util::graph g;
117 
118  // Points.
119  image2d<unsigned> equiv_vertex;
120  initialize(equiv_vertex, work);
121 
122  // Vertices.
123  mln_fwd_piter_(image2d<input_val_t>) p(work.domain());
124  for_all(p)
125  equiv_vertex(p) = g.add_vertex();
126 
127  // Edges.
128  window2d next_c4_win;
129  next_c4_win.insert(0, 1).insert(1, 0);
130  typedef fun::i2v::array<int> edge_values_t;
131  typedef fun::i2v::array< util::site_pair<point2d> > edge_sites_t;
132  edge_values_t edge_values;
133  edge_sites_t edge_sites;
134  mln_fwd_qiter_(window2d) q(next_c4_win, p);
135  for_all (p)
136  for_all(q)
137  if (work.domain().has(q))
138  {
139  g.add_edge(equiv_vertex(p), equiv_vertex(q));
140  edge_values.append(math::max(work(p), work(q)));
141  edge_sites.append(util::site_pair<point2d>(p, q));
142  }
143 
144  // Line graph point set.
145  typedef edge_image<util::site_pair<point2d>,int,util::graph> lg_ima_t;
146  lg_ima_t lg_ima(g, edge_sites, edge_values);
147 
148  /*------.
149  | WST. |
150  `------*/
151 
152  typedef lg_ima_t::nbh_t nbh_t;
153  nbh_t nbh;
154 
155  // Perform a Watershed Transform.
156  int_u8 nbasins;
158  wshed_t wshed = morpho::meyer_wst(lg_ima, nbh, nbasins);
159  mln_assertion(nbasins == 5u);
160 
161  /*---------.
162  | Output. |
163  `---------*/
164 
165  // FIXME: Inlined conversion, to be reifed into a routine.
166 
167  // Save the result in gray levels (data) + color (wshed).
168 
169  // Data.
170  typedef rgb8 output_val_t;
171  typedef image2d<output_val_t> output_t;
172  point2d output_pmin = input.domain().pmin();
173  point2d output_pmax(input.domain().pmax()[0] * 2,
174  input.domain().pmax()[1] * 2);
175  output_t output(box2d(output_pmin, output_pmax));
176  data::fill(output, literal::black);
177  // Reuse the piter on INPUT.
178  for_all(p)
179  {
180  // Equivalent of P in OUTPUT.
181  point2d q(p[0] * 2, p[1] * 2);
182  input_val_t v = input(p);
183  /* FIXME: Use a conversion function from input_val_t to
184  output_val_t instead of an explicit construction. */
185  output(q) = output_val_t(v, v, v);
186  }
187  // Interpolate missing points in OUTPUT.
188  mln_piter_(output_t) p_out(output.domain());
189  for_all(p_out)
190  {
191  // Process points on even rows and odd columns
192  if (p_out[0] % 2 == 0 && p_out[1] % 2 == 1)
193  output(p_out) = (output(p_out + left) + output(p_out + right)) / 2;
194  // Process points on odd rows and even columns
195  if (p_out[0] % 2 == 1 && p_out[1] % 2 == 0)
196  output(p_out) = (output(p_out + up) + output(p_out + down)) / 2;
197  // Process points on odd rows and odd columns
198  if (p_out[0] % 2 == 1 && p_out[1] % 2 == 1)
199  output(p_out) =
200  (output(p_out + dpoint2d(-1, -1)) +
201  output(p_out + dpoint2d(-1, +1)) +
202  output(p_out + dpoint2d(+1, -1)) +
203  output(p_out + dpoint2d(+1, +1))) / 4;
204  }
205  // Draw the watershed.
206  /* FIXME: We should draw the watershed on another image and
207  superimpose it on OUTPUT instead of drawing it directly into
208  OUTPUT. */
209  mln_piter_(wshed_t) pw(wshed.domain());
210  for_all(pw)
211  {
212  if (wshed(pw) == 0u)
213  {
214  mln_psite_(lg_ima_t) pp(pw);
215  // Equivalent of the line (edge) PP in OUTPUT.
216  int row1 = pp.first()[0] * 2;
217  int col1 = pp.first()[1] * 2;
218  int row2 = pp.second()[0] * 2;
219  int col2 = pp.second()[1] * 2;
220  point2d q((row1 + row2) / 2, (col1 + col2) / 2);
221  // Print the watershed in red.
222  output(q) = literal::red;
223  }
224  }
225  // Fill the holes, so that the watershed looks connected.
226  /* FIXME: This approach is bad: it creates thick lines of watershed.
227  We should probably solve this when we ``paint'' the watershed
228  over the ``doubled'' image.
229 
230  A better approach is probably to iterate over the set of vertices,
231  and ``connect'' edges according to patterns (vertically or
232  horizontally connected egdes member of the watershed, etc.). */
233  // Reuse the piter on OUTPUT.
234  for_all (p_out)
235  // Only handle points on odd rows and columns.
236  if (p_out[0] % 2 == 1 && p_out[1] % 2 == 1)
237  {
238  // Count the number of adjacent watershed points. If there are
239  // two or more, consider, create a watershed point.
240  /* FIXME: Iterating over a c4 window would be more elegant, of
241  course. */
242  unsigned nwsheds =
243  (output.has(p_out + up ) && output(p_out + up ) == literal::red) +
244  (output.has(p_out + down ) && output(p_out + down ) == literal::red) +
245  (output.has(p_out + left ) && output(p_out + right) == literal::red) +
246  (output.has(p_out + right) && output(p_out + left ) == literal::red);
247  if (nwsheds >= 2)
248  output(p_out) = literal::red;
249  }
250  io::ppm::save(output, "lena_line_graph_image_wst1-out.ppm");
251 }