$extrastylesheet
Olena  User documentation 2.1
An Image Processing Platform
 All Classes Namespaces Functions Variables Typedefs Enumerations Enumerator Friends Groups Pages
off/load.hh
1 // Copyright (C) 2008, 2009, 2011, 2012 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 #ifndef MLN_IO_OFF_LOAD_HH
28 # define MLN_IO_OFF_LOAD_HH
29 
36 
37 # include <cstdlib>
38 # include <iostream>
39 # include <fstream>
40 # include <string>
41 
42 # include <mln/literal/black.hh>
43 # include <mln/core/concept/object.hh>
44 # include <mln/core/alias/complex_image.hh>
45 
46 
47 namespace mln
48 {
49 
50  namespace io
51  {
52 
53  namespace off
54  {
55 
66  void load(bin_2complex_image3df& ima, const std::string& filename);
67 
68  // FIXME: Implement a load routine for for
69  // int_u8_2complex_image3df.
70 
81  void load(float_2complex_image3df& ima, const std::string& filename);
82 
94  void load(rgb8_2complex_image3df& ima, const std::string& filename);
95 
96 
97  namespace internal
98  {
99 
100  template <typename I, typename E>
101  struct off_loader : public Object<E>
102  {
103  typedef off_loader<I, E> self;
104 
106  static const unsigned D = 2;
108  typedef metal::vec<D + 1, std::vector< mln_value(I) > > values;
110  typedef mln_domain(I) domain;
111 
113  off_loader();
114 
116  void operator()(I& ima, const std::string& filename);
117 
120  static std::istream& eat_comment(std::istream& istr);
121  };
122 
123 
124  struct bin_off_loader
125  : public off_loader< bin_2complex_image3df, bin_off_loader >
126  {
130  void read_face_data(std::istream& istr);
131 
133  void assign(values& vs, const domain& s);
134 
138  void reserve(unsigned nvertices, unsigned nedges, unsigned nfaces);
139  };
140 
141 
142  /* Factor float_off_loader::reserve and rgb8_off_loader::reserve
143  and float_off_loader::assign and rgb8_off_loader::assign
144  by introducing an intermediate class. */
145 
146  struct float_off_loader
147  : public off_loader< float_2complex_image3df, float_off_loader >
148  {
150  void read_face_data(std::istream& istr);
151 
153  void reserve(unsigned nvertices, unsigned nedges, unsigned nfaces);
154 
156  void assign(values& vs, const domain& s);
157 
159  std::vector<float> face_value;
160  };
161 
162 
163  struct rgb8_off_loader
164  : public off_loader< rgb8_2complex_image3df, rgb8_off_loader >
165  {
167  void read_face_data(std::istream& istr);
168 
170  void reserve(unsigned nvertices, unsigned nedges, unsigned nfaces);
171 
173  void assign(values& vs, const domain& s);
174 
176  std::vector<value::rgb8> face_value;
177  };
178 
179  } // end of namespace mln::io::off::internal
180 
181 
182 
183 # ifndef MLN_INCLUDE_ONLY
184 
185  /*----------.
186  | Facades. |
187  `----------*/
188 
189  inline
190  void
191  load(bin_2complex_image3df& ima, const std::string& filename)
192  {
193  mln_trace("mln::io::off::load");
194  internal::bin_off_loader()(ima, filename);
195  }
196 
197  inline
198  void
199  load(float_2complex_image3df& ima, const std::string& filename)
200  {
201  mln_trace("mln::io::off::load");
202  internal::float_off_loader()(ima, filename);
203  }
204 
205  inline
206  void
207  load(rgb8_2complex_image3df& ima, const std::string& filename)
208  {
209  mln_trace("mln::io::off::load");
210  internal::rgb8_off_loader()(ima, filename);
211  }
212 
213 
214 
215  /*-------------------------.
216  | Actual implementations. |
217  `-------------------------*/
218 
219  // -------- //
220  // Canvas. //
221  // -------- //
222 
223  namespace internal
224  {
225 
226  template <typename I, typename E>
227  inline
228  off_loader<I, E>::off_loader()
229  {
230  // Concept checking.
231  void (E::*m1)(std::istream&) = &E::read_face_data;
232  (void) m1;
233  void (E::*m2)(unsigned, unsigned, unsigned) = &E::reserve;
234  (void) m2;
235  void (E::*m3)(values&, const domain&) = &E::assign;
236  (void) m3;
237  }
238 
239 
240  template <typename I, typename E>
241  inline
242  void
243  off_loader<I, E>::operator()(I& ima, const std::string& filename)
244  {
245  const std::string me = "mln::io::off::load";
246 
247  std::ifstream istr(filename.c_str());
248  if (!istr)
249  {
250  std::cerr << me << ": `" << filename << "' not found."
251  << std::endl;
252  /* FIXME: Too violent. We should allow the use of
253  exceptions, at least to have Milena's code behave
254  correctly in interpreted environments (std::exit() or
255  std::abort() causes the termination of a Python
256  interpreter, for instance!). */
257  std::exit(1);
258  }
259 
260  /*---------.
261  | Header. |
262  `---------*/
263 
264  /* ``The .off files in the Princeton Shape Benchmark conform
265  to the following standard''. */
266 
267  /* ``OFF files are all ASCII files beginning with the keyword
268  OFF. '' */
269  std::string type;
270  istr >> &self::eat_comment >> type;
271  if (type != "OFF")
272  {
273  std::cerr << me << ": `" << filename << "': ill-formed header."
274  << std::endl;
275  std::exit(1);
276  }
277 
278  /* ``The next line states the number of vertices, the number
279  of faces, and the number of edges. The number of edges can
280  be safely ignored.'' */
281  unsigned nvertices, nfaces, nedges;
282  istr >> &self::eat_comment >> nvertices
283  >> &self::eat_comment >> nfaces
284  >> &self::eat_comment >> nedges;
285 
287  exact(this)->reserve(nvertices, nedges, nfaces);
288 
289  /*-------.
290  | Data. |
291  `-------*/
292 
293  /* FIXME: Maybe we could sugar all this (using make_whatever
294  helpers?). */
295 
296  // --------- //
297  // Complex. //
298  // --------- //
299 
300  const unsigned D = 2;
301  topo::complex<D> c;
302 
303  // ------------------------------------------ //
304  // Vertices & geometry (vertices locations). //
305  // ------------------------------------------ //
306 
307  /* ``The vertices are listed with x, y, z coordinates, written
308  one per line.'' */
309 
310  typedef point3df P;
311  typedef mln_coord_(P) C;
312  typedef geom::complex_geometry<D, P> G;
313  G geom;
314  geom.reserve(nvertices);
315  for (unsigned v = 0; v < nvertices; ++v)
316  {
317  // Create a 0-face.
318  c.add_face();
319 
320  // Record the geometry (point) associated to this vertex.
321  C x, y, z;
322  istr >> &self::eat_comment >> x
323  >> &self::eat_comment >> y
324  >> &self::eat_comment >> z;
325  geom.add_location(point3df(x, y, z));
326  }
327 
328  // --------------- //
329  // Faces & edges. //
330  // --------------- //
331 
332  /* ``After the list of vertices, the faces are listed, with
333  one face per line. For each face, the number of vertices
334  is specified, followed by indices into the list of
335  vertices.'' */
336 
337  // An adjacenty matrix recording the edges seen so far.
338  typedef std::vector< std::vector<bool> > complex_edges_t;
339  complex_edges_t complex_edges (nvertices,
340  std::vector<bool>(nvertices, false));
341 
342  for (unsigned f = 0; f < nfaces; ++f)
343  {
344  unsigned nface_vertices;
345  istr >> &self::eat_comment >> nface_vertices;
346  if (nface_vertices <= 2)
347  {
348  std::cerr << me << ": `" << filename
349  << "': ill-formed face (having "
350  << nface_vertices << ' '
351  << (nface_vertices < 2 ? "vertex" : "vertices")
352  << ')' << std::endl;
353  std::exit(1);
354  }
355 
356  // The edges of the face.
357  topo::n_faces_set<1, D> face_edges_set;
358  face_edges_set.reserve(nface_vertices);
359 
360  // Remember the first vertex id of the face.
361  unsigned first_vertex_id;
362  istr >> &self::eat_comment >> first_vertex_id;
363  // The current vertex id, initialized with the first id.
364  unsigned vertex_id = first_vertex_id;
365  if (first_vertex_id >= nvertices)
366  {
367  std::cerr << me << ": `" << filename
368  << "': invalid vertex id " << first_vertex_id
369  << std::endl;
370  std::exit(1);
371  }
372  // Iterate on vertices and form edges.
373  for (unsigned v = 0; v < nface_vertices; ++v)
374  {
375  /* The next vertex id. The pair (vertex_id,
376  next_vertex_id) is an edge of the
377  mesh/complex. */
378  unsigned next_vertex_id;
379  /* When V is the id of the last vertex of the face F,
380  set NEXT_VERTEX_ID to FIRST_VERTEX_ID; otherwise,
381  read it from the input. */
382  if (v == nface_vertices - 1)
383  next_vertex_id = first_vertex_id;
384  else
385  {
386  istr >> &self::eat_comment >> next_vertex_id;
387  if (next_vertex_id >= nvertices)
388  {
389  std::cerr << me << ": `" << filename
390  << "': invalid vertex id: "
391  << next_vertex_id << std::endl;
392  std::exit(1);
393  }
394  }
395  // The ends of the current edge.
396  topo::n_face<0, D> vertex(c, vertex_id);
397  topo::n_face<0, D> next_vertex(c, next_vertex_id);
398  // The current edge.
399  topo::algebraic_n_face<1, D> edge;
400  // If the edge has not been constructed yet, create
401  // it; otherwise, retrieve its id from the complex.
402  if (!complex_edges[vertex_id][next_vertex_id])
403  {
404  complex_edges[vertex_id][next_vertex_id] = true;
405  complex_edges[next_vertex_id][vertex_id] = true;
406  edge =
407  make_algebraic_n_face(c.add_face(vertex - next_vertex),
408  true);
409  }
410  else
411  {
412  edge = topo::edge(vertex, next_vertex);
413  mln_assertion(edge.is_valid());
414  }
415  // Record this edge.
416  face_edges_set += edge;
417  // Next vertex.
418  vertex_id = next_vertex_id;
419  }
420 
421  // Possibly read a value (depends on the actual format).
422  exact(this)->read_face_data(istr);
423 
424  // Add face.
425  c.add_face(face_edges_set);
426  }
427 
428  /*--------.
429  | Image. |
430  `--------*/
431 
432  // Site set.
433  domain s(c, geom);
434 
435  // Values.
436  values vs;
437  exact(this)->assign(vs, s);
438 
439  // Image.
440  ima.init_(s, vs);
441 
442  /*--------------.
443  | End of file. |
444  `--------------*/
445 
446  istr >> &self::eat_comment;
447  if (!istr.eof())
448  {
449  std::cerr << me << ": `" << filename
450  << "': end of file not reached" << std::endl;
451  std::exit(1);
452  }
453  istr.close();
454  }
455 
456 
457  // ---------------- //
458  // Specific parts. //
459  // ---------------- //
460 
461  /* FIXME: We do not honor the part
462 
463  ``Line breaks are significant here: the color description
464  begins after VertN and ends with the end of the line (or
465  the next # comment).
466 
467  in the following comment. */
468 
508  inline
509  void
510  bin_off_loader::read_face_data(std::istream& /* istr */)
511  {
512  // Do nothing (no data associated to faces).
513  }
514 
515  inline
516  void
517  float_off_loader::read_face_data(std::istream& istr)
518  {
519  /* We just use R and ignore G, B and A (transparency) when
520  considering the value (``color'') associated to a face as
521  a (scalar) floating-point value (though it really is an
522  RGB triplet).
523 
524  To ensure consistency, we /might/ (later) check that R, G
525  and B are equal---or better, ``almost equal'', as they
526  are floats.
527 
528  Moreover, R must (and G, B and A should) be
529  floating-point values between 0 and 1, according to the
530  OFF file format definition. */
531  // FIXME: `A' should be optional.
532  float r, g, b, a;
533  istr >> r >> g >> b >> a;
534  mln_assertion(0.0f <= r); mln_assertion(r <= 1.0f);
535  mln_assertion(0.0f <= g); mln_assertion(g <= 1.0f);
536  mln_assertion(0.0f <= b); mln_assertion(b <= 1.0f);
537  mln_assertion(0.0f <= a); mln_assertion(a <= 1.0f);
538  face_value.push_back(r);
539  }
540 
541  inline
542  void
543  rgb8_off_loader::read_face_data(std::istream& istr)
544  {
545  /* We just use R, G, and B and ignore A (transparency) when
546  considering the value (``color'') associated to a face.
547 
548  R must (and G, B and A should) be floating-point values
549  between 0 and 1, according to the OFF file format
550  definition. */
551  // FIXME: `A' should be optional.
552  float r, g, b, a;
553  istr >> r >> g >> b >> a;
554  mln_assertion(0.0f <= r); mln_assertion(r <= 1.0f);
555  mln_assertion(0.0f <= g); mln_assertion(g <= 1.0f);
556  mln_assertion(0.0f <= b); mln_assertion(b <= 1.0f);
557  mln_assertion(0.0f <= a); mln_assertion(a <= 1.0f);
558  face_value.push_back(value::rgb8(int(255 * r),
559  int(255 * g),
560  int(255 * b)));
561  }
562  /* \} */
563 
564 
565  inline
566  void
567  bin_off_loader::reserve(unsigned /* nvertices */,
568  unsigned /* nedges */,
569  unsigned /* nfaces */)
570  {
571  // Do nothing (no data associated to faces).
572  }
573 
574  inline
575  void
576  float_off_loader::reserve(unsigned /* nvertices */,
577  unsigned /* nedges */,
578  unsigned nfaces)
579  {
580  face_value.reserve(nfaces);
581  }
582 
583 
584  inline
585  void
586  rgb8_off_loader::reserve(unsigned /* nvertices */,
587  unsigned /* nedges */,
588  unsigned nfaces)
589  {
590  face_value.reserve(nfaces);
591  }
592 
593 
594  inline
595  void
596  bin_off_loader::assign(values& vs, const domain& s)
597  {
598  // Default values.
599  for (unsigned i = 0; i <= D; ++i)
600  vs[i].insert(vs[i].begin(), s.cplx().nfaces_of_dim(i), true);
601  }
602 
603  inline
604  void
605  float_off_loader::assign(values& vs, const domain& s)
606  {
607  // Default values for n-face with n in [0, D[.
608  for (unsigned i = 0; i < D; ++i)
609  vs[i].insert(vs[i].begin(), s.cplx().nfaces_of_dim(i), 0.0f);
610  // Values for D-faces.
611  vs[D] = face_value;
612  }
613 
614  inline
615  void
616  rgb8_off_loader::assign(values& vs, const domain& s)
617  {
618  // Default values for n-face with n in [0, D[.
619  for (unsigned i = 0; i < D; ++i)
620  vs[i].insert(vs[i].begin(), s.cplx().nfaces_of_dim(i),
622  // Values for D-faces.
623  vs[D] = face_value;
624  }
625 
626 
627  // --------- //
628  // Helpers. //
629  // --------- //
630 
631  template <typename I, typename E>
632  inline
633  std::istream&
634  off_loader<I, E>::eat_comment(std::istream& istr)
635  {
636  // Skip whitespace and newlines.
637  std::ws(istr);
638  while (istr.peek() == '#')
639  {
640  /* Eat the `#' and the rest of the line until `\n' or
641  `\r' is found or the end of the file is reached. */
642  char c;
643  do
644  istr.get(c);
645  while (c != '\n' && c != '\r' && !istr.eof());
646  // Skip whitespace and newlines.
647  std::ws(istr);
648  }
649  return istr;
650  }
651 
652  } // end of namespace mln::io::off::internal
653 
654 
655 # endif // ! MLN_INCLUDE_ONLY
656 
657 
658  } // end of namespace mln::io::off
659 
660  } // end of namespace mln::io
661 
662 } // end of namespace mln
663 
664 
665 #endif // ! MLN_IO_OFF_LOAD_HH