$extrastylesheet
Olena  User documentation 2.1
An Image Processing Platform
 All Classes Namespaces Functions Variables Typedefs Enumerations Enumerator Friends Groups Pages
misc.hh
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: Split this file and eventually move its content into the
28 // library.
29 // FIXME: Address license-related issues?
30 
31 #ifndef MILENA_APPS_MESH_SEGM_SKEL_MISC_HH
32 # define MILENA_APPS_MESH_SEGM_SKEL_MISC_HH
33 
34 # include <algorithm> // For std::swap.
35 # include <utility> // For std::pair.
36 
37 # include <mln/algebra/vec.hh>
38 # include <mln/algebra/mat.hh>
39 
40 # include <mln/norm/l2.hh>
41 
42 # include <mln/data/fill.hh>
43 
44 
51 // FIXME: We should turn `likely' and `unlikely' into functions.
52 #ifndef likely
53 # if !defined(__GNUC__) || (__GNUC__ == 2 && __GNUC_MINOR__ < 96)
54 # define likely(x) (x)
55 # define unlikely(x) (x)
56 # else
57 # define likely(x) (__builtin_expect((x), 1))
58 # define unlikely(x) (__builtin_expect((x), 0))
59 # endif
60 #endif
61 
62 namespace mln
63 {
64 
65  namespace algebra
66  {
67 
68  // From Trimesh.
69  // FIXME: Doc (input, return value, etc.).
70  // FIXME: Idea: Add reference to Numerical Recipes.
71 
77  template <unsigned N, typename T>
78  inline
79  bool
81  {
82  vec<N - 1, T> v;
83  for (unsigned i = 0; i < N; ++i)
84  {
85  for (unsigned k = 0; k < i; ++k)
86  v[k] = A(i, k) * rdiag[k];
87  for (unsigned j = i; j < N; ++j)
88  {
89  T sum = A(i, j);
90  for (unsigned k = 0; k < i; k++)
91  sum -= v[k] * A(j, k);
92  if (i == j)
93  {
94  if (unlikely(sum <= T(0)))
95  return false;
96  rdiag[i] = T(1) / sum;
97  }
98  else
99  A(j, i) = sum;
100  }
101  }
102  return true;
103  }
104 
105  // From Trimesh.
106  // FIXME: Doc (input, return value, etc.).
107  // FIXME: Idea: Add reference to Numerical Recipes.
108 
110  template <unsigned N, typename T>
111  inline
112  void
113  ldlt_solve(const mat<N, N, T>& A, const vec<N, T>& rdiag,
114  const vec<N, T>& B, vec<N, T>& x)
115  {
116  for (unsigned i = 0; i < N; ++i)
117  {
118  T sum = B[i];
119  for (unsigned k = 0; k < i; ++k)
120  sum -= A(i, k) * x[k];
121  x[i] = sum * rdiag[i];
122  }
123  for (int i = N - 1; i >= 0; --i)
124  {
125  T sum = 0;
126  for (unsigned k = i + 1; k < N; ++k)
127  sum += A(k, i) * x[k];
128  x[i] -= sum * rdiag[i];
129  }
130  }
131 
132  } // end of namespace mln::algebra
133 
134 } // end of namespace mln
135 
136 
137 // FIXME: To be moved elsewhere (in mln/geom)?
138 
139 // Inspired from Trimesh.
140 
141 namespace mln
142 {
143 
144  namespace geom
145  {
146 
147  // FIXME: More doc (see Trimesh's).
148 
149  /* FIXME: We should restrict attached data to vertices in return
150  value. */
160  inline
163  {
164  // Shortcuts.
165  static const unsigned D = 2;
166  typedef space_2complex_geometry G;
167  typedef algebra::vec<3, float> vec3f;
168  typedef complex_image< D, G, vec3f > normal_t;
169 
170  normal_t normal(mesh);
171  data::fill(normal, literal::zero);
172 
174  // A neighborhood where neighbors are the set of 0-faces
175  // transitively adjacent to the reference point.
176  typedef mln::complex_m_face_neighborhood<D, G> adj_vertices_nbh_t;
177  adj_vertices_nbh_t adj_vertices_nbh;
178  mln_niter_(adj_vertices_nbh_t) adj_v(adj_vertices_nbh, f);
179  /* FIXME: We should be able to pas this value (m) either at the
180  construction of the neighborhood or at the construction of
181  the iterator. Alas, we can't inherit ctors (yet), so we
182  can't rely on a simple mixin approach. */
183  adj_v.iter().set_m(0);
184 
185  // Iterate on 2-faces (triangles).
186  for_all(f)
187  {
189  std::vector<mln_psite_(normal_t)> p;
190  p.reserve(3);
191  for_all(adj_v)
192  p.push_back(adj_v);
194  mln_assertion(p.size() == 3);
195 
196  /* FIXME: Is the explicit conversion to_site() really needed?
197  If not, we could provide a local shortcut like
198 
199  vec(p[0]) // Instead of p[0].to_site().front().to_vec()
200 
201  to shorten these lines. */
202  // FIXME: Factor as well?
203  vec3f a =
204  p[0].to_site().front().to_vec() - p[1].to_site().front().to_vec();
205  vec3f b =
206  p[1].to_site().front().to_vec() - p[2].to_site().front().to_vec();
207  vec3f c =
208  p[2].to_site().front().to_vec() - p[0].to_site().front().to_vec();
209 
210  // FIXME: Factor as well?
211  float l2a = norm::sqr_l2(a);
212  float l2b = norm::sqr_l2(b);
213  float l2c = norm::sqr_l2(c);
214  vec3f face_normal = algebra::vprod(a, b);
215 
216  normal(p[0]) += face_normal * (1.0f / (l2a * l2c));
217  normal(p[1]) += face_normal * (1.0f / (l2b * l2a));
218  normal(p[2]) += face_normal * (1.0f / (l2c * l2b));
219  }
220 
221  // Normalize normals. We don't need to iterate on all faces, just
222  // 0-faces.
224  for_all(v)
225  normal(v).normalize();
226 
227  return normal;
228  }
229 
230 
231  /* FIXME: We should restrict attached data to vertices in return
232  value. */
245  inline
246  std::pair<
249  >
251  {
252  // Shortcuts.
253  static const unsigned D = 2;
254  typedef space_2complex_geometry G;
255  typedef algebra::vec<3, float> vec3f;
256 
257  // Hold data for 2-faces only.
258  typedef complex_image< D, G, vec3f > corner_area_t;
259  // Hold data for 0-faces only.
260  typedef complex_image< D, G, float > point_area_t;
261  // Packed output.
262  typedef std::pair<corner_area_t, point_area_t> output_t;
263 
264  // Initialize output.
265  output_t output(mesh, mesh);
266  corner_area_t& corner_area = output.first;
267  point_area_t& point_area = output.second;
268  data::fill(corner_area, literal::zero);
269  data::fill(point_area, literal::zero);
270 
272  // A neighborhood where neighbors are the set of 0-faces
273  // transitively adjacent to the reference point.
274  typedef mln::complex_m_face_neighborhood<D, G> adj_vertices_nbh_t;
275  adj_vertices_nbh_t adj_vertices_nbh;
276  mln_niter_(adj_vertices_nbh_t) adj_v(adj_vertices_nbh, f);
277  /* FIXME: We should be able to pas this value (m) either at the
278  construction of the neighborhood or at the construction of
279  the iterator. Alas, we can't inherit ctors (yet), so we
280  can't rely on a simple mixin approach. */
281  adj_v.iter().set_m(0);
282 
283  for_all(f)
284  {
286  std::vector<mln_psite_(corner_area_t)> p;
287  p.reserve(3);
288  for_all(adj_v)
289  p.push_back(adj_v);
291  mln_assertion(p.size() == 3);
292 
293  // (Opposite) edge vectors.
295  // FIXME: See above.
296  e.set
297  (p[2].to_site().front().to_vec() - p[1].to_site().front().to_vec(),
298  p[0].to_site().front().to_vec() - p[2].to_site().front().to_vec(),
299  p[1].to_site().front().to_vec() - p[0].to_site().front().to_vec());
300 
301  // Compute corner weights.
302  float area = 0.5f * norm::l2(algebra::vprod(e[0], e[1]));
303  vec3f sqr_norm;
304  sqr_norm.set(norm::sqr_l2(e[0]),
305  norm::sqr_l2(e[1]),
306  norm::sqr_l2(e[2]));
307  vec3f edge_weight;
308  edge_weight.set
309  (sqr_norm[0] * (sqr_norm[1] + sqr_norm[2] - sqr_norm[0]),
310  sqr_norm[1] * (sqr_norm[2] + sqr_norm[0] - sqr_norm[1]),
311  sqr_norm[2] * (sqr_norm[0] + sqr_norm[1] - sqr_norm[2]));
312 
313  if (edge_weight[0] <= 0.0f)
314  {
315  corner_area(f)[1] = -0.25f * sqr_norm[2] * area / (e[0] * e[2]);
316  corner_area(f)[2] = -0.25f * sqr_norm[1] * area / (e[0] * e[1]);
317  corner_area(f)[0] = area - corner_area(f)[1] - corner_area(f)[2];
318  }
319  else if (edge_weight[1] <= 0.0f)
320  {
321  corner_area(f)[2] = -0.25f * sqr_norm[0] * area / (e[1] * e[0]);
322  corner_area(f)[0] = -0.25f * sqr_norm[2] * area / (e[1] * e[2]);
323  corner_area(f)[1] = area - corner_area(f)[2] - corner_area(f)[0];
324  }
325  else if (edge_weight[2] <= 0.0f)
326  {
327  corner_area(f)[0] = -0.25f * sqr_norm[1] * area / (e[2] * e[1]);
328  corner_area(f)[1] = -0.25f * sqr_norm[0] * area / (e[2] * e[0]);
329  corner_area(f)[2] = area - corner_area(f)[0] - corner_area(f)[1];
330  }
331  else
332  {
333  float ewscale =
334  0.5f * area / (edge_weight[0] + edge_weight[1] + edge_weight[2]);
335  for (int i = 0; i < 3; ++i)
336  corner_area(f)[i] =
337  ewscale * (edge_weight[(i+1)%3] + edge_weight[(i+2)%3]);
338  }
339 
340  for (int i = 0; i < 3; ++i)
341  point_area(p[i]) += corner_area(f)[i];
342  }
343 
344  return output;
345  }
346 
347 
348  namespace internal
349  {
350 
366  static inline unsigned next(unsigned i) { return (i + 1) % 3; }
367  static inline unsigned prev(unsigned i) { return (i - 1) % 3; }
371 
372  inline
373  void
375  const algebra::vec<3, float> &old_v,
376  const algebra::vec<3, float> &new_norm,
377  algebra::vec<3, float> &new_u,
378  algebra::vec<3, float> &new_v)
379  {
380  new_u = old_u;
381  new_v = old_v;
382  algebra::vec<3, float> old_norm = vprod(old_u, old_v);
383  float ndot = old_norm * new_norm;
384  if (unlikely(ndot <= -1.0f))
385  {
386  new_u = -new_u;
387  new_v = -new_v;
388  return;
389  }
390  algebra::vec<3, float> perp_old = new_norm - ndot * old_norm;
391  algebra::vec<3, float> dperp =
392  1.0f / (1 + ndot) * (old_norm + new_norm);
393  new_u -= dperp * (new_u * perp_old);
394  new_v -= dperp * (new_v * perp_old);
395  }
396 
397 
398  // FIXME: Add const to formals whenever we can.
399 
400  // Reproject a curvature tensor from the basis spanned by old_u and old_v
401  // (which are assumed to be unit-length and perpendicular) to the
402  // new_u, new_v basis.
403  inline
404  void
406  const algebra::vec<3, float>& old_v,
407  float old_ku, float old_kuv, float old_kv,
408  const algebra::vec<3, float>& new_u,
409  const algebra::vec<3, float>& new_v,
410  float& new_ku, float& new_kuv, float& new_kv)
411  {
412  algebra::vec<3, float> r_new_u, r_new_v;
413  rot_coord_sys(new_u, new_v, vprod(old_u, old_v), r_new_u, r_new_v);
414 
415  float u1 = r_new_u * old_u;
416  float v1 = r_new_u * old_v;
417  float u2 = r_new_v * old_u;
418  float v2 = r_new_v * old_v;
419  new_ku = old_ku * u1*u1 + old_kuv * (2.0f * u1*v1) + old_kv * v1*v1;
420  new_kuv = old_ku * u1*u2 + old_kuv * (u1*v2 + u2*v1) + old_kv * v1*v2;
421  new_kv = old_ku * u2*u2 + old_kuv * (2.0f * u2*v2) + old_kv * v2*v2;
422  }
423 
427  inline
428  void
430  const algebra::vec<3, float>& old_v,
431  float ku, float kuv, float kv,
432  const algebra::vec<3, float>& new_norm,
433  algebra::vec<3, float>& pdir1,
434  algebra::vec<3, float>& pdir2,
435  float& k1, float& k2)
436  {
437  algebra::vec<3, float> r_old_u, r_old_v;
438  rot_coord_sys(old_u, old_v, new_norm, r_old_u, r_old_v);
439 
440  float c = 1, s = 0, tt = 0;
441  if (likely(kuv != 0.0f))
442  {
443  // Jacobi rotation to diagonalize.
444  float h = 0.5f * (kv - ku) / kuv;
445  tt = (h < 0.0f) ?
446  1.0f / (h - sqrt(1.0f + h*h)) :
447  1.0f / (h + sqrt(1.0f + h*h));
448  c = 1.0f / sqrt(1.0f + tt*tt);
449  s = tt * c;
450  }
451 
452  k1 = ku - tt * kuv;
453  k2 = kv + tt * kuv;
454 
455  if (fabs(k1) >= fabs(k2))
456  pdir1 = c*r_old_u - s*r_old_v;
457  else
458  {
459  std::swap(k1, k2);
460  pdir1 = s*r_old_u + c*r_old_v;
461  }
462  pdir2 = vprod(new_norm, pdir1);
463  }
464 
465  } // end of namespace mln::geom::internal
466 
467 
479  /* FIXME: We should restrict attached data to vertices in return
480  value. */
481  /* FIXME: Have a typedef for this return type? */
482  inline
483  std::pair<
486  >
488  {
489  // Shortcuts.
490  static const unsigned D = 2;
491  typedef space_2complex_geometry G;
492  typedef algebra::vec<3, float> vec3f;
493  typedef algebra::mat<3, 3, float> mat3f;
494 
495  typedef complex_image< D, G, vec3f > corner_area_t;
496  typedef complex_image< D, G, float > point_area_t;
497 
498  // Normals at vertices.
500  normal_t normal = mesh_normal(mesh);
501 
502  // Areas ``belonging'' to a normal.
503  typedef complex_image< D, G, vec3f > corner_area_t;
504  typedef complex_image< D, G, float > point_area_t;
505  typedef std::pair<corner_area_t, point_area_t> corner_point_area_t;
506  corner_point_area_t corner_point_area = mesh_corner_point_area(mesh);
507  // Shortcuts.
508  corner_area_t& corner_area = corner_point_area.first;
509  point_area_t& point_area = corner_point_area.second;
510 
511  // Curvatures at each vertex.
513  typedef std::pair<curv_t, curv_t> output_t;
514  output_t output(mesh, mesh);
515  curv_t& curv1 = output.first;
516  curv_t& curv2 = output.second;
517  // ??
519  // Principal directions at each vertex.
522 
523  /*-------------------------------------------------.
524  | Set up an initial coordinate system per vertex. |
525  `-------------------------------------------------*/
526 
528  // A neighborhood where neighbors are the set of 0-faces
529  // transitively adjacent to the reference point.
530  typedef mln::complex_m_face_neighborhood<D, G> adj_vertices_nbh_t;
531  adj_vertices_nbh_t adj_vertices_nbh;
532  mln_niter_(adj_vertices_nbh_t) adj_v(adj_vertices_nbh, f);
533  /* FIXME: We should be able to pas this value (m) either at the
534  construction of the neighborhood or at the construction of
535  the iterator. Alas, we can't inherit ctors (yet), so we
536  can't rely on a simple mixin approach. */
537  adj_v.iter().set_m(0);
538 
539  for_all(f)
540  {
542  std::vector<mln_psite_(curv_t)> p;
543  p.reserve(3);
544  for_all(adj_v)
545  p.push_back(adj_v);
547  mln_assertion(p.size() == 3);
548 
549  // FIXME: See above.
550  pdir1(p[0]) =
551  p[1].to_site().front().to_vec() - p[0].to_site().front().to_vec();
552  pdir1(p[1]) =
553  p[2].to_site().front().to_vec() - p[1].to_site().front().to_vec();
554  pdir1(p[2]) =
555  p[0].to_site().front().to_vec() - p[2].to_site().front().to_vec();
556  }
557 
559  for_all(v)
560  {
561  pdir1(v) = algebra::vprod(pdir1(v), normal(v));
562  pdir1(v).normalize();
563  pdir2(v) = algebra::vprod(normal(v), pdir1(v));
564  }
565 
566  /*-----------------------------.
567  | Compute curvature per-face. |
568  `-----------------------------*/
569 
570  for_all(f)
571  {
572  std::vector<mln_psite_(curv_t)> p;
573  p.reserve(3);
574  for_all(adj_v)
575  p.push_back(adj_v);
576  mln_assertion(p.size() == 3);
577 
578  // (Opposite) edge vectors.
580  // FIXME: See above.
581  e.set
582  (p[2].to_site().front().to_vec() - p[1].to_site().front().to_vec(),
583  p[0].to_site().front().to_vec() - p[2].to_site().front().to_vec(),
584  p[1].to_site().front().to_vec() - p[0].to_site().front().to_vec());
585 
586  // N-T-B coordinate system per face.
587  vec3f t = e[0];
588  t.normalize();
589  vec3f n = algebra::vprod(e[0], e[1]);
590  /* FIXME: Why don't we normalize N? Is it a normal vector by
591  construction? Or maybe we don't need it to be normal? */
592  vec3f b = algebra::vprod(n, t);
593  b.normalize();
594 
595  // Estimate curvature based on variation of normals along edges.
596  vec3f m = literal::zero;
597  mat3f w = literal::zero;
598  for (int j = 0; j < 3; ++j)
599  {
600  float u = e[j] * t;
601  float v = e[j] * b;
602  w(0, 0) += u * u;
603  w(0, 1) += u * v;
604  /* FIXME: Those two lines were commented in Trimesh's
605  original code.
606 
607  //w(1, 1) += v*v + u*u;
608  //w(1, 2) += u*v;
609 
610  */
611  w(2, 2) += v * v;
612  vec3f dn =
613  normal(p[internal::prev(j)]) - normal(p[internal::next(j)]);
614  float dnu = dn * t;
615  float dnv = dn * b;
616  m[0] += dnu*u;
617  m[1] += dnu*v + dnv*u;
618  m[2] += dnv*v;
619  }
620  w(1, 1) = w(0, 0) + w(2, 2);
621  w(1, 2) = w(0, 1);
622 
623  // Least squares solution.
624  vec3f diag;
625  // Note that algebra::ldlt_decomp has side effects on its
626  // arguments; this call is not ``just an assertion''.
627  bool ldlt_decomp_success_p = algebra::ldlt_decomp(w, diag);
628  mln_assertion(ldlt_decomp_success_p);
629  // Avoid a warning about an undefined variable when NDEBUG
630  // is not defined.
631  (void) ldlt_decomp_success_p;
632  algebra::ldlt_solve(w, diag, m, m);
633 
634  // Push it back out to the vertices
635  for (int j = 0; j < 3; ++j)
636  {
637  float c1, c12, c2;
638  internal::proj_curv(t, b, m[0], m[1], m[2],
639  pdir1(p[j]), pdir2(p[j]), c1, c12, c2);
640  float wt = corner_area(f)[j] / point_area(p[j]);
641  curv1(p[j]) += wt * c1;
642  curv12(p[j]) += wt * c12;
643  curv2(p[j]) += wt * c2;
644  }
645  }
646 
647  /*-------------------------------------------------------------.
648  | Compute principal directions and curvatures at each vertex. |
649  `-------------------------------------------------------------*/
650 
651  for_all(v)
652  internal::diagonalize_curv(pdir1(v), pdir2(v),
653  curv1(v), curv12(v), curv2(v),
654  normal(v), pdir1(v), pdir2(v),
655  curv1(v), curv2(v));
656 
657  return output;
658  }
659 
660  } // end of namespace mln::geom
661 
662 } // end of namespace mln
663 
664 #endif // ! MILENA_APPS_MESH_SEGM_SKEL_MISC_HH