ImplicitSurfaceUtils.h
1 /*
2  * Medical Image Registration ToolKit (MIRTK)
3  *
4  * Copyright 2013-2015 Imperial College London
5  * Copyright 2013-2015 Andreas Schuh
6  *
7  * Licensed under the Apache License, Version 2.0 (the "License");
8  * you may not use this file except in compliance with the License.
9  * You may obtain a copy of the License at
10  *
11  * http://www.apache.org/licenses/LICENSE-2.0
12  *
13  * Unless required by applicable law or agreed to in writing, software
14  * distributed under the License is distributed on an "AS IS" BASIS,
15  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
16  * See the License for the specific language governing permissions and
17  * limitations under the License.
18  */
19 
20 #ifndef MIRTK_ImplicitSurfaceUtils_H
21 #define MIRTK_ImplicitSurfaceUtils_H
22 
23 #include "mirtk/Assert.h"
24 #include "mirtk/Math.h"
25 #include "mirtk/Array.h"
26 #include "mirtk/Algorithm.h"
27 #include "mirtk/GenericImage.h"
28 #include "mirtk/RegisteredImage.h"
29 #include "mirtk/InterpolateImageFunction.h"
30 #include "mirtk/ImageGradientFunction.h"
31 #include "mirtk/PointSamples.h"
32 #include "mirtk/PointSetUtils.h"
33 
34 // Include inline definitions of image interpolation functions
35 #include "mirtk/LinearInterpolateImageFunction.hxx"
36 #include "mirtk/FastLinearImageGradientFunction.hxx"
37 
38 #include "vtkSmartPointer.h"
39 #include "vtkPolyData.h"
40 
41 
42 namespace mirtk {
43 
44 
45 // TODO: Wrap these functions in a class named ImplicitSurface, which is
46 // a subclass of GenericImage and maintains its own interpolators.
47 namespace ImplicitSurfaceUtils {
48 
49 
50 // =============================================================================
51 // Typedefs
52 // =============================================================================
53 
54 typedef GenericImage<RegisteredImage::VoxelType> DistanceImage;
55 typedef GenericLinearInterpolateImageFunction<DistanceImage> DistanceFunction;
56 typedef GenericFastLinearImageGradientFunction<DistanceImage> DistanceGradient;
57 
58 // =============================================================================
59 // Contouring
60 // =============================================================================
61 
62 // -----------------------------------------------------------------------------
63 /// Extract isosurface from distance image
64 ///
65 /// @param[in] dmap Distance image.
66 /// @param[in] offset Isovalue.
67 /// @param[in] blurring Standard deviation of Gaussian kernel with which to
68 /// blur the distance image before resampling/contouring.
69 /// @param[in] isotropic Whether to resample distance image to isotropic voxel size.
70 /// @param[in] close Whether to close surface at boundary of image domain.
71 /// @param[in] normals Whether to compute normal vectors at mesh points.
72 /// @param[in] gradients Whether to compute gradient vectors at mesh points.
73 ///
74 /// @returns Discrete isosurface mesh.
75 vtkSmartPointer<vtkPolyData> Isosurface(const DistanceImage &dmap, double offset = .0,
76  double blurring = .0, bool isotropic = true, bool close = true,
77  bool normals = true, bool gradients = false);
78 
79 // =============================================================================
80 // Implicit surface distance function
81 // =============================================================================
82 
83 // -----------------------------------------------------------------------------
84 /// Get distance value at given world position
85 inline double Evaluate(const DistanceFunction &dist, const double p[3], double offset = .0)
86 {
87  double d, x = p[0], y = p[1], z = p[2];
88  dist.WorldToImage(x, y, z);
89  if (dist.IsInside(x, y, z)) {
90  d = dist.GetInside(x, y, z);
91  } else {
92  double xmin, ymin, zmin, xmax, ymax, zmax;
93  dist.Inside(xmin, ymin, zmin, xmax, ymax, zmax);
94  x = clamp(x, xmin, xmax);
95  y = clamp(y, ymin, ymax);
96  z = clamp(z, zmin, zmax);
97  d = max(0., static_cast<double>(dist.GetInside(x, y, z)));
98  dist.ImageToWorld(x, y, z);
99  x -= p[0], y -= p[1], z -= p[2];
100  d += sqrt(x*x + y*y + z*z);
101  }
102  return d - offset;
103 }
104 
105 // -----------------------------------------------------------------------------
106 /// Get (normalized) distance gradient at given world position
107 inline void Evaluate(const DistanceGradient &gradient, const double p[3], double g[3], bool normalize = true)
108 {
109  double x = p[0], y = p[1], z = p[2];
110  gradient.WorldToImage(x, y, z);
111  if (gradient.IsInside(x, y, z)) {
112  DistanceGradient::GradientType v = gradient.GetInside(x, y, z);
113  if (normalize) v.Normalize();
114  g[0] = static_cast<double>(v._x);
115  g[1] = static_cast<double>(v._y);
116  g[2] = static_cast<double>(v._z);
117  } else {
118  g[0] = g[1] = g[2] = .0;
119  }
120 }
121 
122 // =============================================================================
123 // Auxiliary functions for finding implicit surface intersections
124 // =============================================================================
125 
126 // -----------------------------------------------------------------------------
127 /// Bracket zero crossing along specified ray
128 inline bool BracketZeroCrossing(const double p[3], const double e[3],
129  double minh, double maxh,
130  double &a, double &da, double &b, double &db,
131  const DistanceFunction &distance, double offset,
132  double tol = 1e-3)
133 {
134  if (fequal(da, .0, tol)) {
135  b = a, db = da;
136  return true;
137  }
138  if (abs(da) < maxh) {
139  double x[3], step = max(minh, abs(da));
140  for (b = step; b <= maxh; b += step) {
141  x[0] = p[0] + b * e[0];
142  x[1] = p[1] + b * e[1];
143  x[2] = p[2] + b * e[2];
144  db = Evaluate(distance, x, offset);
145  if (fequal(db, .0, tol) || da * db <= .0) return true; // zero crossing
146  a = b, da = db;
147  step = max(minh, abs(da));
148  }
149  }
150  return false;
151 }
152 
153 // -----------------------------------------------------------------------------
154 /// Find zero crossing in [a, b] using binary search
155 inline double BinarySearch(const double p[3], const double e[3],
156  double a, double da, double b, double db,
157  const DistanceFunction &distance, double offset = .0,
158  double tol = 1e-3)
159 {
160  if (fequal(da, .0, tol)) return a;
161  if (fequal(db, .0, tol)) return b;
162  mirtkAssert(da * db < .0, "[a, b] brackets zero crossing");
163  double x[3], d, h = a;
164  while (a < b) {
165  h = .5 * (a + b);
166  x[0] = p[0] + h * e[0];
167  x[1] = p[1] + h * e[1];
168  x[2] = p[2] + h * e[2];
169  d = Evaluate(distance, x, offset);
170  if (fequal(d, .0, tol)) break;
171  if (da * d < .0) b = h, db = d;
172  else a = h, da = d;
173  }
174  return h;
175 }
176 
177 // -----------------------------------------------------------------------------
178 /// Find zero crossing in [a, b] using minimum surface distance as adaptive step length
179 inline double AdaptiveSearch(const double p[3], const double e[3],
180  double a, double da, double b, double db,
181  const DistanceFunction &distance, double offset = .0,
182  double tol = 1e-3)
183 {
184  if (fequal(da, .0, tol)) return a;
185  if (fequal(db, .0, tol)) return b;
186  mirtkAssert(da * db < .0, "[a, b] brackets zero crossing");
187  double x[3], h, d = da, step = abs(da), prev;
188  for (h = a + step; h <= b; h += step) {
189  prev = abs(d);
190  x[0] = p[0] + h * e[0];
191  x[1] = p[1] + h * e[1];
192  x[2] = p[2] + h * e[2];
193  d = Evaluate(distance, x, offset);
194  if (prev - abs(d) < tol) return h;
195  step = copysign(abs(d), da * d);
196  }
197  return b;
198 }
199 
200 // =============================================================================
201 // Implicit surface intersections
202 // =============================================================================
203 
204 // -----------------------------------------------------------------------------
205 /// Find closest intersection of line segment with implicit surface
206 ///
207 /// \returns Distance of intersection point from point \p p. If the line segment
208 /// does not intersect the surface, \p maxh is returned.
209 inline double IntersectWithLine(const double p[3], const double e[3],
210  double d0, double minh, double maxh,
211  const DistanceFunction &distance, double offset = .0,
212  double tol = 1e-3)
213 {
214  if (fequal(d0, .0, tol)) return .0;
215  double a = .0, da = d0, b, db;
216  if (!BracketZeroCrossing(p, e, minh, maxh, a, da, b, db, distance, offset, tol)) {
217  return maxh;
218  }
219 #if 1
220  return BinarySearch(p, e, a, da, b, db, distance, offset, tol);
221 #else
222  return AdaptiveSearch(p, e, a, da, b, db, distance, offset, tol);
223 #endif
224 }
225 
226 // -----------------------------------------------------------------------------
227 /// Determine distances to all isosurface intersections with a given line segment
228 inline int Intersections(Array<double> &distances, const double p[3], const double e1[3],
229  double d0, double minh, double l,
230  const DistanceFunction &distance, double offset = .0,
231  double tol = 1e-3)
232 {
233  double x[3] = {p[0], p[1], p[2]}, d = .0, h, maxh = l;
234  distances.clear();
235  if (fequal(d0, .0, tol)) {
236  distances.push_back(.0);
237  while (fequal(d0, .0, tol)) {
238  d += minh;
239  x[0] += minh * e1[0];
240  x[1] += minh * e1[1];
241  x[2] += minh * e1[2];
242  maxh -= minh;
243  if (maxh <= minh) break;
244  d0 = Evaluate(distance, x, offset);
245  }
246  }
247  while (maxh > minh) {
248  d += (h = IntersectWithLine(x, e1, d0, minh, maxh, distance, offset, tol));
249  if (h == maxh) break;
250  distances.push_back(d);
251  if (h != .0) {
252  x[0] += h * e1[0];
253  x[1] += h * e1[1];
254  x[2] += h * e1[2];
255  maxh -= h;
256  d0 = Evaluate(distance, x, offset);
257  }
258  while (fequal(d0, .0, tol)) {
259  d += minh;
260  x[0] += minh * e1[0];
261  x[1] += minh * e1[1];
262  x[2] += minh * e1[2];
263  maxh -= minh;
264  if (maxh <= minh) break;
265  d0 = Evaluate(distance, x, offset);
266  }
267  }
268  return static_cast<int>(distances.size());
269 }
270 
271 // -----------------------------------------------------------------------------
272 /// Determine distances to all isosurface intersections with a given line segment
273 inline int Intersections(Array<double> &d, const double p[3], const double e[3],
274  double minh, double l,
275  const DistanceFunction &distance, double offset = .0,
276  double tol = 1e-3)
277 {
278  const double d0 = Evaluate(distance, p, offset);
279  return Intersections(d, p, e, d0, minh, l, distance, offset, tol);
280 }
281 
282 // =============================================================================
283 // Directional surface distance (vs. minimum distance)
284 // =============================================================================
285 
286 // -----------------------------------------------------------------------------
287 /// Determine surface distance at a point in a specified direction
288 ///
289 /// \returns Surface distance in specified direction, clamped to \p maxd.
290 inline double ForwardDistance(const double p[3], const double e1[3],
291  double mind, double minh, double maxd,
292  const DistanceFunction &distance, double offset = .0,
293  double tol = 1e-3)
294 {
295  return IntersectWithLine(p, e1, mind, minh, maxd, distance, offset, tol);
296 }
297 
298 // -----------------------------------------------------------------------------
299 /// Determine surface distance at a point in a specified direction
300 ///
301 /// \returns Surface distance in specified direction, clamped to \p maxd.
302 inline double ForwardDistance(const double p[3], const double e1[3],
303  double minh, double maxd,
304  const DistanceFunction &distance, double offset = .0,
305  double tol = 1e-3)
306 {
307  const double mind = Evaluate(distance, p, offset);
308  return ForwardDistance(p, e1, mind, minh, maxd, distance, offset, tol);
309 }
310 
311 // -----------------------------------------------------------------------------
312 /// Determine surface distance at a point in a specified direction
313 ///
314 /// \returns Surface distance in specified direction, clamped to \p maxd.
315 inline double BackwardDistance(const double p[3], const double e1[3],
316  double mind, double minh, double maxd,
317  const DistanceFunction &distance, double offset = .0,
318  double tol = 1e-3)
319 {
320  const double e2[3] = {-e1[0], -e1[1], -e1[2]};
321  return IntersectWithLine(p, e2, mind, minh, maxd, distance, offset, tol);
322 }
323 
324 // -----------------------------------------------------------------------------
325 /// Determine surface distance at a point in a specified direction
326 ///
327 /// \returns Surface distance in specified direction, clamped to \p maxd.
328 inline double BackwardDistance(const double p[3], const double e1[3],
329  double minh, double maxd,
330  const DistanceFunction &distance, double offset = .0,
331  double tol = 1e-3)
332 {
333  const double mind = Evaluate(distance, p, offset);
334  return BackwardDistance(p, e1, mind, minh, maxd, distance, offset, tol);
335 }
336 
337 // -----------------------------------------------------------------------------
338 /// Determine surface distance at a point in a specified direction
339 ///
340 /// \returns Surface distance in specified direction, clamped to \p maxd.
341 inline double Distance(const double p[3], const double e1[3],
342  double mind, double minh, double maxd,
343  const DistanceFunction &distance, double offset = .0,
344  double tol = 1e-3)
345 {
346  double d1 = ForwardDistance (p, e1, mind, minh, maxd, distance, offset, tol);
347  double d2 = BackwardDistance(p, e1, mind, minh, maxd, distance, offset, tol);
348  return min(d1, d2);
349 }
350 
351 // -----------------------------------------------------------------------------
352 /// Determine surface distance at a point in a specified direction
353 ///
354 /// \returns Surface distance in specified direction, clamped to \p maxd.
355 inline double Distance(const double p[3], const double e1[3],
356  double minh, double maxd,
357  const DistanceFunction &distance, double offset = .0,
358  double tol = 1e-3)
359 {
360  const double mind = Evaluate(distance, p, offset);
361  return Distance(p, e1, mind, minh, maxd, distance, offset, tol);
362 }
363 
364 // -----------------------------------------------------------------------------
365 /// Determine signed surface distance at a point in normal direction
366 ///
367 /// \returns Signed surface distance in normal direction, clamped to \p maxd.
368 inline double SignedDistance(const double p[3], const double n[3],
369  double mind, double minh, double maxd,
370  const DistanceFunction &distance, double offset = .0,
371  double tol = 1e-3)
372 {
373  if (mind < .0) return -ForwardDistance(p, n, mind, minh, maxd, distance, offset, tol);
374  else return BackwardDistance(p, n, mind, minh, maxd, distance, offset, tol);
375 }
376 
377 // -----------------------------------------------------------------------------
378 /// Determine signed surface distance at a point in normal direction
379 ///
380 /// \returns Signed surface distance in normal direction, clamped to \p maxw.
381 inline double SignedDistance(const double p[3], const double n[3],
382  double minh, double maxd,
383  const DistanceFunction &distance, double offset = .0,
384  double tol = 1e-3)
385 {
386  const double mind = Evaluate(distance, p, offset);
387  return SignedDistance(p, n, mind, minh, maxd, distance, offset, tol);
388 }
389 
390 // -----------------------------------------------------------------------------
391 /// Determine width of implicit surface at a point in a specified direction
392 ///
393 /// The width of the implicit surface is the sum of the distances to the nearest
394 /// surface points along a ray cast in opposing directions. The starting point
395 /// can be either in a convex region outside the implicit surface or a concave
396 /// region inside of it.
397 ///
398 /// \returns Width of implicit surface in given direction, clamped to \p maxw.
399 /// If either ray does not intersect the implicit surface, \p maxw is returned.
400 inline double Width(const double p[3], const double e1[3],
401  double mind, double minh, double maxw,
402  const DistanceFunction &distance, double offset = .0,
403  double tol = 1e-3)
404 {
405  double d1 = ForwardDistance(p, e1, mind, minh, maxw, distance, offset, tol);
406  if (d1 >= maxw) return maxw;
407  double d2 = BackwardDistance(p, e1, mind, minh, maxw, distance, offset, tol);
408  return min(d1 + d2, maxw);
409 }
410 
411 // -----------------------------------------------------------------------------
412 /// Determine width of implicit surface at a point in a specified direction
413 ///
414 /// The width of the implicit surface is the sum of the distances to the nearest
415 /// surface points along a ray cast in opposing directions. The starting point
416 /// can be either in a convex region outside the implicit surface or a concave
417 /// region inside of it.
418 ///
419 /// \returns Width of implicit surface in given direction, clamped to \p maxw.
420 /// If either ray does not intersect the implicit surface, \p maxw is returned.
421 inline double Width(const double p[3], const double e1[3],
422  double minh, double maxw,
423  const DistanceFunction &distance, double offset = .0,
424  double tol = 1e-3)
425 {
426  const double mind = Evaluate(distance, p, offset);
427  return Width(p, e1, mind, minh, maxw, distance, offset, tol);
428 }
429 
430 // =============================================================================
431 // Miscellaneous distance measurements (e.g., width of cortical sulcus)
432 // =============================================================================
433 
434 // -----------------------------------------------------------------------------
435 /// Base class of distance measurement functors
437 {
438  /// Distance measurements
439  mirtkAttributeMacro(Array<double>, Values);
440 
441 protected:
442 
443  /// Construct distance measurement for given number of values
444  DistanceMeasurement(int nvalues = 1)
445  :
446  _Values(nvalues, numeric_limits<double>::quiet_NaN())
447  {}
448 
449  /// Destructor
450  virtual ~DistanceMeasurement() {}
451 
452 public:
453 
454  /// Get number of distance measurements
455  int NumberOfValues() const
456  {
457  return static_cast<int>(_Values.size());
458  }
459 
460  /// Set distance value(s)
461  void Set(double v)
462  {
463  for (size_t i = 0; i < _Values.size(); ++i) {
464  _Values[i] = v;
465  }
466  }
467 
468  /// Set distance value(s)
469  void Set(int i, double v)
470  {
471  _Values[i] = v;
472  }
473 
474  /// Get i-th distance measurement
475  double Get(int i = 0) const
476  {
477  return _Values[i];
478  }
479 
480  /// Determine distance value(s) in given directions
481  ///
482  /// \param[in] p Center point at which to evaluate width of gap.
483  /// \param[in] dirs Point samples on unit sphere centered at the origin.
484  /// \param[in] mind Minimum implicit surface distance at point \p p.
485  /// \param[in] minh Minimum step length for bracketing of intersections.
486  /// \param[in] maxd Maximum distance considered for ray casting.
487  /// \param[in] distance Implicit surface distance function.
488  /// \param[in] offset Isovalue of implicit surface.
489  /// \param[in] tol Tolerance by which distance value may differ from \p offset.
490  virtual void Evaluate(const double p[3], const PointSamples &dirs,
491  double mind, double minh, double maxd,
492  const DistanceFunction &distance, double offset = .0,
493  double tol = 1e-3) = 0;
494 
495  /// Determine distance value(s) in given directions
496  ///
497  /// \param[in] p Center point at which to evaluate width of gap.
498  /// \param[in] dirs Point samples on unit sphere centered at the origin.
499  /// \param[in] minh Minimum step length for bracketing of intersections.
500  /// \param[in] maxd Maximum distance considered for ray casting.
501  /// \param[in] distance Implicit surface distance function.
502  /// \param[in] offset Isovalue of implicit surface.
503  /// \param[in] tol Tolerance by which distance value may differ from \p offset.
504  virtual void Evaluate(const double p[3], const PointSamples &dirs,
505  double minh, double maxd,
506  const DistanceFunction &distance, double offset = .0,
507  double tol = 1e-3)
508  {
509  const double mind = ImplicitSurfaceUtils::Evaluate(distance, p, offset);
510  if (abs(mind) < tol) {
511  Set(.0); // point lies on isosurface
512  } else if (abs(mind) >= maxd) {
513  Set(maxd); // point is too far from isosurface
514  } else {
515  this->Evaluate(p, dirs, mind, minh, maxd, distance, offset, tol);
516  }
517  }
518 };
519 
520 // -----------------------------------------------------------------------------
521 /// Determine minimum of the widths measured in multiple directions
523 {
525  void Evaluate(const double p[3], const PointSamples &dirs,
526  double mind, double minh, double maxw,
527  const DistanceFunction &distance, double offset = .0,
528  double tol = 1e-3)
529  {
530  double e[3], w = maxw;
531  for (int i = 0; i < dirs.Size(); ++i) {
532  dirs.GetPoint(i, e);
533  w = min(w, Width(p, e, mind, minh, maxw, distance, offset, tol));
534  }
535  Set(0, w);
536  }
537 };
538 
539 // -----------------------------------------------------------------------------
540 /// Determine maximum of the widths measured in multiple directions
542 {
544  void Evaluate(const double p[3], const PointSamples &dirs,
545  double mind, double minh, double maxw,
546  const DistanceFunction &distance, double offset = .0,
547  double tol = 1e-3)
548  {
549  double e[3], w = .0;
550  for (int i = 0; i < dirs.Size(); ++i) {
551  dirs.GetPoint(i, e);
552  w = max(w, Width(p, e, mind, minh, maxw, distance, offset, tol));
553  }
554  Set(0, w);
555  }
556 };
557 
558 // -----------------------------------------------------------------------------
559 /// Determine maximum of the widths measured in multiple directions
561 {
563 
565  void Evaluate(const double p[3], const PointSamples &dirs,
566  double mind, double minh, double maxw,
567  const DistanceFunction &distance, double offset = .0,
568  double tol = 1e-3)
569  {
570  double e[3], w, wmin = maxw, wmax = .0;
571  for (int i = 0; i < dirs.Size(); ++i) {
572  dirs.GetPoint(i, e);
573  w = Width(p, e, mind, minh, maxw, distance, offset, tol);
574  if (w < wmin) wmin = w;
575  if (w > wmax) wmax = w;
576  }
577  Set(0, wmin);
578  Set(1, wmax);
579  }
580 
581  double Min() const
582  {
583  return Get(0);
584  }
585 
586  double Max() const
587  {
588  return Get(1);
589  }
590 };
591 
592 /// Alternative name for WidthExtrema
593 typedef WidthExtrema MinMaxWidth;
594 
595 // -----------------------------------------------------------------------------
596 /// Determine average of the widths measured in multiple directions
598 {
600  void Evaluate(const double p[3], const PointSamples &dirs,
601  double mind, double minh, double maxw,
602  const DistanceFunction &distance, double offset = .0,
603  double tol = 1e-3)
604  {
605  double e[3], w = .0;
606  for (int i = 0; i < dirs.Size(); ++i) {
607  dirs.GetPoint(i, e);
608  w += Width(p, e, mind, minh, maxw, distance, offset, tol);
609  }
610  Set(0, w / dirs.Size());
611  }
612 };
613 
614 // -----------------------------------------------------------------------------
615 /// Determine average of the widths measured in multiple directions
617 {
619  void Evaluate(const double p[3], const PointSamples &dirs,
620  double mind, double minh, double maxw,
621  const DistanceFunction &distance, double offset = .0,
622  double tol = 1e-3)
623  {
624  double e[3];
625  Array<double> w(dirs.Size());
626  for (int i = 0; i < dirs.Size(); ++i) {
627  dirs.GetPoint(i, e);
628  w[i] = Width(p, e, mind, minh, maxw, distance, offset, tol);
629  }
630  sort(w.begin(), w.end());
631  Set(0, w[w.size() / 2]);
632  }
633 };
634 
635 // =============================================================================
636 // Auxiliary functions to evaluate distance measurements
637 //
638 // TODO: Consider to make these member functions of the base class DistanceMeasurement.
639 // =============================================================================
640 
641 // -----------------------------------------------------------------------------
642 /// Obtain value of distance measurement evaluated in multiple directions
643 ///
644 /// \param[in] p Center point at which to evaluate width of gap.
645 /// \param[in] dirs Point samples on unit sphere centered at the origin.
646 /// \param[in] mind Minimum implicit surface distance at point \p p.
647 /// \param[in] minh Minimum step length for bracketing of intersections.
648 /// \param[in] maxh Maximum distance considered for ray casting.
649 /// \param[in] distance Implicit surface distance function.
650 /// \param[in] offset Isovalue of implicit surface.
651 /// \param[in] tol Tolerance by which distance value may differ from \p offset.
652 ///
653 /// \returns Measured distance value.
654 ///
655 /// \see MinWidth, MaxWidth, MeanWidth, MedianWidth
656 template <class DistanceMeasurement>
657 inline double Evaluate(const double p[3], const PointSamples &dirs,
658  double mind, double minh, double maxh,
659  const DistanceFunction &distance, double offset = .0,
660  double tol = 1e-3)
661 {
663  d.Evaluate(p, dirs, mind, minh, maxh, distance, offset, tol);
664  return d.Get(0);
665 }
666 
667 // -----------------------------------------------------------------------------
668 /// Obtain value of distance measurement evaluated in multiple directions
669 ///
670 /// \param[in] p Center point at which to evaluate width of gap.
671 /// \param[in] dirs Point samples on unit sphere centered at the origin.
672 /// \param[in] minh Minimum step length for bracketing of intersections.
673 /// \param[in] maxh Maximum distance considered for ray casting.
674 /// \param[in] distance Implicit surface distance function.
675 /// \param[in] offset Isovalue of implicit surface.
676 /// \param[in] tol Tolerance by which distance value may differ from \p offset.
677 ///
678 /// \returns Measured distance value clamped to \p maxw.
679 /// When the point \p p lies on the isosurface, zero is returned.
680 ///
681 /// \see MinWidth, MaxWidth, MeanWidth, MedianWidth
682 template <class DistanceMeasurement>
683 inline double Evaluate(const double p[3], const PointSamples &dirs,
684  double minh, double maxh,
685  const DistanceFunction &distance, double offset = .0,
686  double tol = 1e-3)
687 {
689  d.Evaluate(p, dirs, minh, maxh, distance, offset, tol);
690  return d.Get(0);
691 }
692 
693 // -----------------------------------------------------------------------------
694 /// Obtain value(s) of distance measurement evaluated in spherical directions
695 ///
696 /// \param[out] d Distance measurement(s).
697 /// \param[in] p Center point at which to evaluate width of gap.
698 /// \param[in] mind Minimum implicit surface distance at point \p p.
699 /// \param[in] minh Minimum step length for bracketing of intersections.
700 /// \param[in] maxh Maximum distance considered for ray casting.
701 /// \param[in] distance Implicit surface distance function.
702 /// \param[in] offset Isovalue of implicit surface.
703 /// \param[in] tol Tolerance by which distance value may differ from \p offset.
704 /// \param[in] ndirs Desired number of spherical direction samples.
705 ///
706 /// \see MinWidth, MaxWidth, MeanWidth, MedianWidth
707 ///
708 /// \note When evaluating the distance measure at multiple points, it is
709 /// more efficient to precompute the spherical direction samples using
710 /// the PointSamples::SampleRegularHalfSphere function and to call
711 /// DistanceMeasurement::Evaluate directly with these as argument.
712 template <class DistanceMeasurement>
713 inline void EvaluateSpherical(DistanceMeasurement &d, const double p[3],
714  double mind, double minh, double maxh,
715  const DistanceFunction &distance, double offset = .0,
716  double tol = 1e-3, int ndirs = 20)
717 {
718  PointSamples dirs(ndirs);
720  d.Evaluate(p, dirs, mind, minh, maxh, distance, offset, tol);
721 }
722 
723 // -----------------------------------------------------------------------------
724 /// Obtain value of distance measurement evaluated in spherical directions
725 ///
726 /// \param[in] p Center point at which to evaluate width of gap.
727 /// \param[in] n Face normal vector.
728 /// \param[in] mind Minimum implicit surface distance at point \p p.
729 /// \param[in] minh Minimum step length for bracketing of intersections.
730 /// \param[in] maxh Maximum distance considered for ray casting.
731 /// \param[in] distance Implicit surface distance function.
732 /// \param[in] offset Isovalue of implicit surface.
733 /// \param[in] tol Tolerance by which distance value may differ from \p offset.
734 /// \param[in] ndirs Desired number of spherical direction samples.
735 ///
736 /// \returns Measured distance value in tangent plane.
737 ///
738 /// \see MinWidth, MaxWidth, MeanWidth, MedianWidth
739 ///
740 /// \note When evaluating the distance measure at multiple points, it is
741 /// more efficient to precompute the spherical direction samples using
742 /// the PointSamples::SampleRegularHalfSphere function and to call
743 /// DistanceMeasurement::Evaluate directly with these as argument.
744 template <class DistanceMeasurement>
745 inline double EvaluateSpherical(const double p[3], const double n[3],
746  double mind, double minh, double maxh,
747  const DistanceFunction &distance, double offset = .0,
748  double tol = 1e-3, int ndirs = 20)
749 {
751  EvaluateSpherical(d, p, n, mind, minh, maxh, distance, offset, tol, ndirs);
752  return d.Get(0);
753 }
754 
755 // -----------------------------------------------------------------------------
756 /// Obtain value(s) of distance measurement evaluated in tangent directions
757 ///
758 /// \param[out] d Distance measurement(s).
759 /// \param[in] p Center point at which to evaluate width of gap.
760 /// \param[in] n Face normal vector.
761 /// \param[in] mind Minimum implicit surface distance at point \p p.
762 /// \param[in] minh Minimum step length for bracketing of intersections.
763 /// \param[in] maxh Maximum distance considered for ray casting.
764 /// \param[in] distance Implicit surface distance function.
765 /// \param[in] offset Isovalue of implicit surface.
766 /// \param[in] tol Tolerance by which distance value may differ from \p offset.
767 /// \param[in] ndirs Number of direction samples in tangent plane.
768 /// \param[in] beta Maximum angular deviation of normal vector.
769 ///
770 /// \see MinWidth, MaxWidth, MeanWidth, MedianWidth
771 template <class DistanceMeasurement>
772 inline void EvaluateTangential(DistanceMeasurement &d,
773  const double p[3], const double n[3],
774  double mind, double minh, double maxh,
775  const DistanceFunction &distance, double offset = .0,
776  double tol = 1e-3, int ndirs = 4, double beta = .0)
777 {
778  double e1[3], e2[3];
779  if (!ComputeTangents(n, e1, e2)) {
780  d.Set(numeric_limits<double>::quiet_NaN());
781  return;
782  }
783  const int nbetas = (beta == .0 ? 1 : 3);
784  PointSamples dirs(ndirs * nbetas);
785  switch (ndirs) {
786  case 1: {
787  dirs(0) = Point(e1);
788  } break;
789  case 2: {
790  dirs(0) = Point(e1);
791  dirs(1) = Point(e2);
792  } break;
793  case 4: {
794  const double sqrt2 = sqrt(2.0);
795  dirs(0) = Point(e1);
796  dirs(1) = Point(e2);
797  dirs(2) = (Point(e1) + Point(e2)) / sqrt2;
798  dirs(3) = (Point(e1) - Point(e2)) / sqrt2;
799  } break;
800  default:
801  double alpha = .0, delta = pi / ndirs;
802  for (int i = 0; i < ndirs; ++i, alpha += delta) {
803  dirs(i) = Point(e1) * cos(alpha) + Point(e2) * sin(alpha);
804  }
805  break;
806  }
807  if (nbetas > 1) {
808  beta *= rad_per_deg;
809  Point sin_beta_n(n);
810  sin_beta_n *= sin(beta);
811  const double cos_beta = cos(beta);
812  for (int i = 0, j = ndirs; i < ndirs; ++i) {
813  dirs(j++) = dirs(i) * cos_beta + sin_beta_n;
814  dirs(j++) = dirs(i) * cos_beta - sin_beta_n;
815  }
816  }
817  d.Evaluate(p, dirs, mind, minh, maxh, distance, offset, tol);
818 }
819 
820 // -----------------------------------------------------------------------------
821 /// Obtain value of distance measurement evaluated in tangent directions
822 ///
823 /// \param[in] p Center point at which to evaluate width of gap.
824 /// \param[in] n Face normal vector.
825 /// \param[in] mind Minimum implicit surface distance at point \p p.
826 /// \param[in] minh Minimum step length for bracketing of intersections.
827 /// \param[in] maxh Maximum distance considered for ray casting.
828 /// \param[in] distance Implicit surface distance function.
829 /// \param[in] offset Isovalue of implicit surface.
830 /// \param[in] tol Tolerance by which distance value may differ from \p offset.
831 /// \param[in] ndirs Number of direction samples in tangent plane.
832 /// \param[in] beta Maximum angular deviation of normal vector.
833 ///
834 /// \returns Measured distance value in tangent plane.
835 ///
836 /// \see MinWidth, MaxWidth, MeanWidth, MedianWidth
837 template <class DistanceMeasurement>
838 inline double EvaluateTangential(const double p[3], const double n[3],
839  double mind, double minh, double maxh,
840  const DistanceFunction &distance, double offset = .0,
841  double tol = 1e-3, int ndirs = 4, double beta = .0)
842 {
844  EvaluateTangential(d, p, n, mind, minh, maxh, distance, offset, tol, ndirs, beta);
845  return d.Get(0);
846 }
847 
848 // -----------------------------------------------------------------------------
849 /// Obtain value of distance measurement evaluated in tangent directions
850 ///
851 /// \param[out] d Distance measurement(s).
852 /// \param[in] p Center point at which to evaluate width of gap.
853 /// \param[in] n Face normal vector.
854 /// \param[in] minh Minimum step length for bracketing of intersections.
855 /// \param[in] maxh Maximum distance considered for ray casting.
856 /// \param[in] distance Implicit surface distance function.
857 /// \param[in] offset Isovalue of implicit surface.
858 /// \param[in] tol Tolerance by which distance value may differ from \p offset.
859 /// \param[in] ndirs Number of direction samples in tangent plane.
860 /// \param[in] beta Maximum angular deviation of normal vector.
861 ///
862 /// \see MinWidth, MaxWidth, MeanWidth, MedianWidth
863 template <class DistanceMeasurement>
864 inline void EvaluateTangential(DistanceMeasurement &d,
865  const double p[3], const double n[3],
866  double minh, double maxh,
867  const DistanceFunction &distance, double offset = .0,
868  double tol = 1e-3, int ndirs = 4, double beta = .0)
869 {
870  const double mind = Evaluate(distance, p, offset);
871  if (abs(mind) >= maxh) {
872  d.Set(maxh); // point is too far from isosurface
873  // Note: abs(mind) < tol is not used in this case because the tangent vectors
874  // can be orthogonal to the tangent plane of the isosurface. In this case,
875  // we still might want to measure some distance in the direction of the
876  // outwards (or inwards) pointing isosurface normal.
877  } else if (Distance(p, n, mind, minh, 2.0 * tol + tol, distance, offset, .5 * tol) <= tol) {
878  d.Set(.0); // point is either very close to or lies on the isosurface
879  } else {
880  EvaluateTangential(d, p, n, mind, minh, maxh, distance, offset, tol, ndirs, beta);
881  }
882 }
883 
884 // -----------------------------------------------------------------------------
885 /// Obtain value of distance measurement evaluated in tangent directions
886 ///
887 /// \param[in] p Center point at which to evaluate width of gap.
888 /// \param[in] n Face normal vector.
889 /// \param[in] minh Minimum step length for bracketing of intersections.
890 /// \param[in] maxh Maximum distance considered for ray casting.
891 /// \param[in] distance Implicit surface distance function.
892 /// \param[in] offset Isovalue of implicit surface.
893 /// \param[in] tol Tolerance by which distance value may differ from \p offset.
894 /// \param[in] ndirs Number of direction samples in tangent plane.
895 /// \param[in] beta Maximum angular deviation of normal vector.
896 ///
897 /// \returns Measured distance value clamped to \p maxh.
898 /// When the point \p p lies on the isosurface, zero is returned.
899 ///
900 /// \see MinWidth, MaxWidth, MeanWidth, MedianWidth
901 template <class DistanceMeasurement>
902 inline double EvaluateTangential(const double p[3], const double n[3],
903  double minh, double maxh,
904  const DistanceFunction &distance, double offset = .0,
905  double tol = 1e-3, int ndirs = 4, double beta = .0)
906 {
908  EvaluateTangential(d, p, n, minh, maxh, distance, offset, tol, ndirs, beta);
909  return d.Get(0);
910 }
911 
912 // -----------------------------------------------------------------------------
913 /// Obtain for all voxels a distance measurement in spherical directions
914 ///
915 /// What distance measurement is obtained depends on the type of the template
916 /// function argument, such as for example the MinWidth.
917 ///
918 /// \returns Image with measured distance values for each voxel in the distance image.
919 ///
920 /// \see MinWidth, MaxWidth, MeanWidth, MedianWidth
921 template <class DistanceMeasurement>
922 DistanceImage Evaluate(double maxh, const DistanceImage &dmap,
923  double offset = .0, double tol = 1e-3, int ndirs = 20)
924 {
925  const double ds = min(min(dmap.GetXSize(), dmap.GetYSize()), dmap.GetZSize());
926  const double minh = .1 * ds;
927 
928  PointSamples dirs(ndirs);
930 
932  DistanceImage output(dmap.Attributes(), d.NumberOfValues());
933 
934  DistanceFunction distance;
935  distance.Input(&dmap);
936  distance.Initialize();
937 
938  double p[3];
939  for (int k = 0; k < output.Z(); ++k)
940  for (int j = 0; j < output.Y(); ++j)
941  for (int i = 0; i < output.X(); ++i) {
942  p[0] = i, p[1] = j, p[2] = k;
943  output.ImageToWorld(p[0], p[1], p[2]);
944  d.Evaluate(p, dirs, minh, maxh, distance, offset, tol);
945  for (int l = 0; l < output.T(); ++l) {
946  output(i, j, k, l) = static_cast<DistanceImage::VoxelType>(d.Get(l));
947  }
948  }
949 
950  return output;
951 }
952 
953 
954 } } // namespace mirtk::ImplicitSurfaceUtils
955 
956 #endif // MIRTK_ImplicitSurfaceUtils_H
bool ComputeTangents(const double n[3], double e1[3], double e2[3])
void Evaluate(const double p[3], const PointSamples &dirs, double mind, double minh, double maxw, const DistanceFunction &distance, double offset=.0, double tol=1e-3)
const ImageAttributes & Attributes() const
Gets the image attributes.
Definition: BaseImage.h:856
double GetYSize() const
Returns the size of a voxel in the y-direction.
Definition: BaseImage.h:958
void Evaluate(const double p[3], const PointSamples &dirs, double mind, double minh, double maxw, const DistanceFunction &distance, double offset=.0, double tol=1e-3)
void SampleRegularHalfSphere(double r=1.0)
Add regular spherical point samples.
void Evaluate(const double p[3], const PointSamples &dirs, double mind, double minh, double maxw, const DistanceFunction &distance, double offset=.0, double tol=1e-3)
MIRTK_Common_EXPORT const double pi
Constant value of .
int NumberOfValues() const
Get number of distance measurements.
void Set(int i, double v)
Set distance value(s)
Base class of distance measurement functors.
virtual void Evaluate(const double p[3], const PointSamples &dirs, double mind, double minh, double maxd, const DistanceFunction &distance, double offset=.0, double tol=1e-3)=0
double Get(int i=0) const
Get i-th distance measurement.
MIRTKCU_API bool fequal(double a, double b, double tol=1e-12)
Definition: Math.h:138
Definition: IOConfig.h:41
Determine maximum of the widths measured in multiple directions.
Point & GetPoint(int)
Get n-th point.
Definition: PointSet.h:330
Determine maximum of the widths measured in multiple directions.
void Normalize()
Normalize vector to length one.
Definition: Vector3D.h:907
Array< T >::iterator BinarySearch(Array< T > &values, const T &value)
Find value in sorted Array using binary search.
Definition: Algorithm.h:269
double GetZSize() const
Returns the size of a voxel in the z-direction.
Definition: BaseImage.h:964
DistanceMeasurement(int nvalues=1)
Construct distance measurement for given number of values.
virtual void Evaluate(const double p[3], const PointSamples &dirs, double minh, double maxd, const DistanceFunction &distance, double offset=.0, double tol=1e-3)
void Size(int)
Definition: PointSet.h:434
double GetXSize() const
Returns the size of a voxel in the x-direction.
Definition: BaseImage.h:952
void Evaluate(const double p[3], const PointSamples &dirs, double mind, double minh, double maxw, const DistanceFunction &distance, double offset=.0, double tol=1e-3)
virtual void Input(const BaseImage *)
Set input image.
void Evaluate(const double p[3], const PointSamples &dirs, double mind, double minh, double maxw, const DistanceFunction &distance, double offset=.0, double tol=1e-3)
Determine average of the widths measured in multiple directions.
Determine average of the widths measured in multiple directions.
TVoxel VoxelType
Voxel type.
Definition: GenericImage.h:52
Vector3D< double > GradientType
Type of interpolated gradient vectors.
MIRTK_Common_EXPORT const double rad_per_deg
Radians per degree, i.e., .
Determine minimum of the widths measured in multiple directions.