Milena (Olena)  User documentation 2.0a Id
 All Classes Namespaces Functions Variables Typedefs Enumerator Groups Pages
apps/papers/levillain.09.ismm/graph.cc
1 // Copyright (C) 2008, 2009 EPITA Research and Development Laboratory (LRDE)
2 //
3 // This file is part of Olena.
4 //
5 // Olena is free software: you can redistribute it and/or modify it under
6 // the terms of the GNU General Public License as published by the Free
7 // Software Foundation, version 2 of the License.
8 //
9 // Olena is distributed in the hope that it will be useful,
10 // but WITHOUT ANY WARRANTY; without even the implied warranty of
11 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
12 // General Public License for more details.
13 //
14 // You should have received a copy of the GNU General Public License
15 // along with Olena. If not, see <http://www.gnu.org/licenses/>.
16 //
17 // As a special exception, you may use this file as part of a free
18 // software project without restriction. Specifically, if other files
19 // instantiate templates or use macros or inline functions from this
20 // file, or you compile this file and link it with other files to produce
21 // an executable, this file does not by itself cause the resulting
22 // executable to be covered by the GNU General Public License. This
23 // exception does not however invalidate any other reasons why the
24 // executable file might be covered by the GNU General Public License.
25 
26 #include <iostream>
27 #include <iomanip>
28 
29 #include <vector>
30 
31 #include <mln/core/image/image2d.hh>
32 #include <mln/core/routine/duplicate.hh>
33 #include <mln/core/alias/neighb2d.hh>
34 #include <mln/core/site_set/p_queue_fast.hh>
35 #include <mln/labeling/blobs.hh>
36 #include <mln/io/pgm/load.hh>
37 #include <mln/debug/println.hh>
38 #include <mln/draw/line.hh>
39 #include <mln/pw/all.hh>
40 #include <mln/binarization/threshold.hh>
41 
42 #include <mln/value/int_u8.hh>
43 #include <mln/value/label_8.hh>
44 #include <mln/core/alias/point2d.hh>
45 
46 #include <mln/core/site_set/p_faces.hh>
47 #include <mln/core/image/complex_image.hh>
48 #include <mln/core/alias/complex_geometry.hh>
49 #include <mln/core/alias/complex_image.hh>
50 
51 // FIXME: Include these elsewhere? (In complex_image.hh?)
52 #include <mln/core/image/complex_neighborhoods.hh>
53 #include <mln/core/image/complex_neighborhood_piter.hh>
54 #include <mln/core/image/complex_windows.hh>
55 
56 #include <mln/data/fill.hh>
57 #include <mln/morpho/gradient.hh>
58 #include <mln/labeling/colorize.hh>
59 
60 #include "chain.hh"
61 
62 
63 // FIXME: Copied and adjusted from pics/graph.cc; factor.
64 
65 namespace mln
66 {
67 
68  template <typename I, typename N>
69  mln_concrete(I)
70  influence_zones(const I& input, const N& nbh)
71  {
72  mln_concrete(I) output = duplicate(input);
73 
74  p_queue_fast<mln_site(I)> q;
75 
76  {
77  // Initialization.
78  mln_piter(I) p(input.domain());
79  mln_niter(N) n(nbh, p);
80  for_all(p)
81  if (input(p) != 0)
82  for_all(n) if (input.has(n))
83  if (input(n) == 0)
84  {
85  q.push(p);
86  break;
87  }
88  }
89  {
90  // Body.
91  mln_site(I) p;
92  mln_niter(N) n(nbh, p);
93  while (! q.is_empty())
94  {
95  p = q.pop_front();
96  mln_invariant(output(p) != 0);
97  for_all(n) if (input.has(n))
98  if (output(n) == 0)
99  {
100  output(n) = output(p);
101  q.push(n);
102  }
103  }
104  }
105 
106  return output;
107 
108  }
109 
110 
111  namespace io
112  {
113 
114  namespace neato
115  {
116 
117  /* FIXME: This is just the gray-level version. Handle other value
118  types as well. */
119  void
120  save(const complex_image<1, discrete_plane_1complex_geometry,
121  value::int_u8>& ima,
122  const std::string& filename,
123  const std::string& bgcolor = "#0000C0",
124  const std::string& fontcolor = "#0000C0",
125  bool empty_vertex_label = true)
126  {
127  typedef value::int_u8 V;
128  typedef complex_image<1, discrete_plane_1complex_geometry, V> I;
129  const unsigned D = 1;
131 
132  std::ofstream g(filename.c_str());
133  g << "graph wst" << std::endl
134  << "{" << std::endl
135  << " graph [bgcolor = \"" << bgcolor << "\"]" << std::endl
136  << " edge [color = \"#FFFFFF\"]" << std::endl
137  << " node [color = \"#FFFFFF\", height=\"5\", width=\"5\","
138  << " fontsize=\"100\", fontcolor = \"" << fontcolor << "\"]"
139  << std::endl;
140 
141  // Vertices.
142  p_n_faces_fwd_piter<D, G> v(ima.domain(), 0);
143  typedef complex_higher_neighborhood<D, G> e_nbh_t;
144  e_nbh_t e_nbh;
145  for_all(v)
146  {
147  V vertex_color = ima(v);
148  std::ostringstream vertex_color_str;
149  // FIXME: Only valid for gray-level images.
150  vertex_color_str << '#'
151  << std::hex
152  << std::setfill('0')
153  << std::setw(2) << vertex_color
154  << std::setw(2) << vertex_color
155  << std::setw(2) << vertex_color
156  << std::dec;
157 
158  g << " v" << v.unproxy_().face_id()
159  << " [pos = \""
160  << std::fixed << std::setprecision(1)
161  << (float)v.to_site().front()[1] << ", "
162  << -(float)v.to_site().front()[0]
163  << "\", color = \"" << vertex_color_str.str()
164  << "\", fillcolor = \"" << vertex_color_str.str()
165  << "\", pin = \"true\", style=\"filled,setlinewidth(3)\"";
166  if (empty_vertex_label)
167  g << ", label = \"\"";
168  g << "];"
169  << std::endl;
170  }
171 
172  // Edges.
173  p_n_faces_fwd_piter<D, G> e(ima.domain(), 1);
174  typedef complex_lower_neighborhood<D, G> v_nbh_t;
175  v_nbh_t v_nbh;
176  mln_niter_(v_nbh_t) adj_v(v_nbh, e);
177  for_all(e)
178  {
179  V edge_color = ima(e);
180  std::ostringstream edge_color_str;
181  edge_color_str << '#'
182  << std::hex
183  << std::setfill('0')
184  << std::setw(2) << edge_color
185  << std::setw(2) << edge_color
186  << std::setw(2) << edge_color
187  << std::dec;
188 
189  // Adjacent vertices.
190  adj_v.start();
191  topo::face<1> v1 = adj_v.unproxy_().face();
192  point2d p1 = adj_v.to_site().front();
193  adj_v.next();
194  topo::face<1> v2 = adj_v.unproxy_().face();
195  point2d p2 = adj_v.to_site().front();
196  adj_v.next();
197  mln_invariant(!adj_v.is_valid());
198 
199  g << " v" << v1.face_id() << " -- v" << v2.face_id() << " ";
200  g << "[color = \"" << edge_color_str.str()
201  << "\", style=\"setlinewidth(10)\"];" << std::endl;
202  }
203 
204  g << "}" << std::endl;
205  g.close();
206  }
207 
208  // FIXME: Factor with the previous version.
209  void
210  save(const complex_image<1, discrete_plane_1complex_geometry,
211  value::rgb8>& ima,
212  const std::string& filename,
213  const std::string& bgcolor = "#0000C0",
214  const std::string& fontcolor = "#0000C0",
215  bool empty_vertex_label = true)
216  {
217  typedef value::rgb8 V;
218  typedef complex_image<1, discrete_plane_1complex_geometry, V> I;
219  const unsigned D = 1;
221 
222  std::ofstream g(filename.c_str());
223  g << "graph wst" << std::endl
224  << "{" << std::endl
225  << " graph [bgcolor = \"" << bgcolor << "\"]" << std::endl
226  << " edge [color = \"#FFFFFF\"]" << std::endl
227  << " node [color = \"#FFFFFF\", height=\"5\", width=\"5\","
228  << " fontsize=\"100\", fontcolor = \"" << fontcolor << "\"]"
229  << std::endl;
230 
231  // Vertices.
232  p_n_faces_fwd_piter<D, G> v(ima.domain(), 0);
233  typedef complex_higher_neighborhood<D, G> e_nbh_t;
234  e_nbh_t e_nbh;
235  for_all(v)
236  {
237  V vertex_color = ima(v);
238  std::ostringstream vertex_color_str;
239  // FIXME: Only valid for gray-level images.
240  vertex_color_str << '#'
241  << std::hex
242  << std::setfill('0')
243  << std::setw(2) << vertex_color.red()
244  << std::setw(2) << vertex_color.green()
245  << std::setw(2) << vertex_color.blue()
246  << std::dec;
247 
248  g << " v" << v.unproxy_().face_id()
249  << " [pos = \""
250  << std::fixed << std::setprecision(1)
251  << (float)v.to_site().front()[1] << ", "
252  << -(float)v.to_site().front()[0]
253  << "\", color = \"" << vertex_color_str.str()
254  << "\", fillcolor = \"" << vertex_color_str.str()
255  << "\", pin = \"true\", style=\"filled,setlinewidth(3)\"";
256  if (empty_vertex_label)
257  g << ", label = \"\"";
258  g << "];"
259  << std::endl;
260  }
261 
262  // Edges.
263  p_n_faces_fwd_piter<D, G> e(ima.domain(), 1);
264  typedef complex_lower_neighborhood<D, G> v_nbh_t;
265  v_nbh_t v_nbh;
266  mln_niter_(v_nbh_t) adj_v(v_nbh, e);
267  for_all(e)
268  {
269  V edge_color = ima(e);
270  std::ostringstream edge_color_str;
271  edge_color_str << '#'
272  << std::hex
273  << std::setfill('0')
274  << std::setw(2) << edge_color.red()
275  << std::setw(2) << edge_color.green()
276  << std::setw(2) << edge_color.blue()
277  << std::dec;
278 
279  // Adjacent vertices.
280  adj_v.start();
281  topo::face<1> v1 = adj_v.unproxy_().face();
282  point2d p1 = adj_v.to_site().front();
283  adj_v.next();
284  topo::face<1> v2 = adj_v.unproxy_().face();
285  point2d p2 = adj_v.to_site().front();
286  adj_v.next();
287  mln_invariant(!adj_v.is_valid());
288 
289  g << " v" << v1.face_id() << " -- v" << v2.face_id() << " ";
290  g << "[color = \"" << edge_color_str.str()
291  << "\", style=\"setlinewidth(10)\"];" << std::endl;
292  }
293 
294  g << "}" << std::endl;
295  g.close();
296  }
297 
298  } // end of namespace mln::io::neato
299 
300  } // end of namespace mln::io
301 
302 } // end of namespace mln
303 
304 
305 // FIXME: Clean up and move into Milena?
307 make_complex_image(const mln::image2d<mln::value::int_u8>& input)
308 {
309  using namespace mln;
310  using mln::value::int_u8;
311 
312  /*----------------------------------------.
313  | Complex + complex geometry (location). |
314  `----------------------------------------*/
315 
316  border::thickness = 0;
317 
318  unsigned nlabels;
319  image2d<unsigned> label =
320  labeling::blobs(mln::binarization::threshold(input, 1), c4(), nlabels);
321 
322  std::cout << "n seeds = " << nlabels << std::endl;
323  {
324  image2d<int_u8> lab(label.domain());
325  data::paste(label, lab);
326  }
327 
328  image2d<unsigned> iz = influence_zones(label, c4());
329  {
330  image2d<int_u8> IZ(iz.domain());
331  data::paste(iz, IZ);
332  }
333 
334 
335  // Make graph/complex.
336 
337  std::vector< std::vector<bool> > adj(nlabels + 1);
338  for (unsigned l = 1; l <= nlabels; ++l)
339  adj[l].resize(nlabels + 1, false);
340 
341  {
342  box2d::piter p(iz.domain());
343  for_all(p)
344  {
345  point2d r = p + right, b = p + down;
346  if (iz.has(r) && iz(p) != iz(r))
347  {
348  if (iz(p) <= iz(r))
349  adj[iz(p)][iz(r)] = true;
350  else
351  adj[iz(r)][iz(p)] = true;
352  }
353  if (iz.has(b) && iz(p) != iz(b))
354  {
355  if (iz(p) <= iz(b))
356  adj[iz(p)][iz(b)] = true;
357  else
358  adj[iz(b)][iz(p)] = true;
359  }
360  }
361  }
362 
363  // end of mk_graph
364 
365 
366  const unsigned D = 1;
367 
369 
370  typedef point2d P;
372  G geom;
373 
374  // Convenience typedefs.
375  typedef topo::n_face<0, D> vertex;
376  typedef topo::n_face<1, D> edge;
377 
378  {
379 
380  // 0-faces (vertices).
381  std::vector<vertex> v;
382  {
383  box2d::piter p(label.domain());
384  for_all(p)
385  if (label(p) != 0)
386  {
387  geom.add_location(p);
388  v.push_back(c.add_face());
389  }
390  }
391 
392  std::cout << "v size = " << v.size() << std::endl;
393 
394  // 1-faces (edges).
395  std::vector<edge> e;
396  {
397  for (unsigned l = 1; l <= nlabels; ++l)
398  for (unsigned ll = l + 1; ll <= nlabels; ++ll)
399  if (adj[l][ll])
400  e.push_back( c.add_face(-v[l-1] + v[ll-1]) );
401  }
402 
403  std::cout << "e size = " << e.size() << std::endl;
404 
405  }
406 
407 
408  /*---------------------.
409  | Complex-based pset. |
410  `---------------------*/
411 
412  p_complex<D, G> pc(c, geom);
413 
414  /*----------------------.
415  | Complex-based image. |
416  `----------------------*/
417 
418  // An image type built on a 1-complex with unsigned values on each
419  // face (both vertices and edges).
420  typedef complex_image<D, G, int_u8> output_t;
421 
422  // Create and initialize an image based on PC.
423  output_t output(pc);
424 
425  // Color OUTPUT's vertices with INPUT's values.
426  p_n_faces_fwd_piter<D, G> v(output.domain(), 0);
427  for_all(v)
428  output(v) = input(v.to_site().front());
429 
430  // Use a medium gray to color edges.
431  p_n_faces_fwd_piter<D, G> e(output.domain(), 1);
432  for_all(e)
433  output(e) = 128;
434 
435  return output;
436 }
437 
438 
439 // FIXME: Move to Milena?
440 template <unsigned D, typename G, typename V>
441 mln::complex_higher_dim_connected_n_face_window_p<D, G>
442 make_elt_win(const mln::complex_image<D, G, V>& /* ima */)
443 {
444  return mln::complex_higher_dim_connected_n_face_window_p<D, G>();
445 }
446 
447 // FIXME: Move to Milena?
448 template <unsigned D, typename G, typename V>
449 mln::complex_higher_dim_connected_n_face_neighborhood<D, G>
450 make_elt_nbh(const mln::complex_image<D, G, V>& /* ima */)
451 {
452  return mln::complex_higher_dim_connected_n_face_neighborhood<D, G>();
453 }
454 
455 
456 int main(int argc, char* argv[])
457 {
458  if (argc != 4)
459  {
460  std::cerr << "usage: " << argv[0] << " seeds.pgm lambda output.neato"
461  << std::endl;
462  std::exit(1);
463  }
464  std::string input_filename = argv[1];
465  unsigned lambda = atoi(argv[2]);
466  std::string output_filename = argv[3];
467 
468  using namespace mln;
469  using mln::value::int_u8;
470  // Use clearer names.
471  typedef int_u8_1complex_image2d int_u8_graph_image2d;
472 
473  typedef int_u8_graph_image2d input;
474  typedef value::label_8 label;
475  typedef mln_ch_value_(input, label) output;
476  label nbasins;
477 
478  // Seeds.
479  image2d<int_u8> seeds = io::pgm::load<int_u8>(input_filename);
480  // Input image computed from the graph of influence zones (IZ) of seeds.
481  typedef int_u8_graph_image2d ima_t;
482  ima_t ima = make_complex_image(seeds);
483  io::neato::save(ima, "apps/graph.neato");
484 
485  // Gradient.
486  input g = morpho::gradient(ima, make_elt_win(ima));
487 
488 #if 0
489  // FIXME: get the name as argument.
490  io::neato::save(g, "apps/graph-g.neato");
491 #endif
492 
493  // Chain.
494  output s = chain(g, make_elt_nbh(g), lambda, nbasins);
495  io::neato::save(labeling::colorize(value::rgb8(), s, nbasins),
496  output_filename);
497 }