20 #ifndef MIRTK_ImplicitSurfaceUtils_H 21 #define MIRTK_ImplicitSurfaceUtils_H 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" 35 #include "mirtk/LinearInterpolateImageFunction.hxx" 36 #include "mirtk/FastLinearImageGradientFunction.hxx" 38 #include "vtkSmartPointer.h" 39 #include "vtkPolyData.h" 47 namespace ImplicitSurfaceUtils {
54 typedef GenericImage<RegisteredImage::VoxelType> DistanceImage;
55 typedef GenericLinearInterpolateImageFunction<DistanceImage> DistanceFunction;
56 typedef GenericFastLinearImageGradientFunction<DistanceImage> DistanceGradient;
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);
85 inline double Evaluate(
const DistanceFunction &dist,
const double p[3],
double offset = .0)
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);
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);
107 inline void Evaluate(
const DistanceGradient &gradient,
const double p[3],
double g[3],
bool normalize =
true)
109 double x = p[0], y = p[1], z = p[2];
110 gradient.WorldToImage(x, y, z);
111 if (gradient.IsInside(x, y, z)) {
114 g[0] =
static_cast<double>(v._x);
115 g[1] =
static_cast<double>(v._y);
116 g[2] =
static_cast<double>(v._z);
118 g[0] = g[1] = g[2] = .0;
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,
134 if (
fequal(da, .0, tol)) {
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;
147 step = max(minh, abs(da));
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,
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;
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;
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,
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) {
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);
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,
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)) {
220 return BinarySearch(p, e, a, da, b, db, distance, offset, tol);
222 return AdaptiveSearch(p, e, a, da, b, db, distance, offset, tol);
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,
233 double x[3] = {p[0], p[1], p[2]}, d = .0, h, maxh = l;
235 if (
fequal(d0, .0, tol)) {
236 distances.push_back(.0);
237 while (
fequal(d0, .0, tol)) {
239 x[0] += minh * e1[0];
240 x[1] += minh * e1[1];
241 x[2] += minh * e1[2];
243 if (maxh <= minh)
break;
244 d0 = Evaluate(distance, x, offset);
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);
256 d0 = Evaluate(distance, x, offset);
258 while (
fequal(d0, .0, tol)) {
260 x[0] += minh * e1[0];
261 x[1] += minh * e1[1];
262 x[2] += minh * e1[2];
264 if (maxh <= minh)
break;
265 d0 = Evaluate(distance, x, offset);
268 return static_cast<int>(distances.size());
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,
278 const double d0 = Evaluate(distance, p, offset);
279 return Intersections(d, p, e, d0, minh, l, distance, offset, tol);
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,
295 return IntersectWithLine(p, e1, mind, minh, maxd, distance, offset, tol);
302 inline double ForwardDistance(
const double p[3],
const double e1[3],
303 double minh,
double maxd,
304 const DistanceFunction &distance,
double offset = .0,
307 const double mind = Evaluate(distance, p, offset);
308 return ForwardDistance(p, e1, mind, minh, maxd, distance, offset, tol);
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,
320 const double e2[3] = {-e1[0], -e1[1], -e1[2]};
321 return IntersectWithLine(p, e2, mind, minh, maxd, distance, offset, tol);
328 inline double BackwardDistance(
const double p[3],
const double e1[3],
329 double minh,
double maxd,
330 const DistanceFunction &distance,
double offset = .0,
333 const double mind = Evaluate(distance, p, offset);
334 return BackwardDistance(p, e1, mind, minh, maxd, distance, offset, tol);
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,
346 double d1 = ForwardDistance (p, e1, mind, minh, maxd, distance, offset, tol);
347 double d2 = BackwardDistance(p, e1, mind, minh, maxd, distance, offset, tol);
355 inline double Distance(
const double p[3],
const double e1[3],
356 double minh,
double maxd,
357 const DistanceFunction &distance,
double offset = .0,
360 const double mind = Evaluate(distance, p, offset);
361 return Distance(p, e1, mind, minh, maxd, distance, offset, tol);
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,
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);
381 inline double SignedDistance(
const double p[3],
const double n[3],
382 double minh,
double maxd,
383 const DistanceFunction &distance,
double offset = .0,
386 const double mind = Evaluate(distance, p, offset);
387 return SignedDistance(p, n, mind, minh, maxd, distance, offset, tol);
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,
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);
421 inline double Width(
const double p[3],
const double e1[3],
422 double minh,
double maxw,
423 const DistanceFunction &distance,
double offset = .0,
426 const double mind = Evaluate(distance, p, offset);
427 return Width(p, e1, mind, minh, maxw, distance, offset, tol);
439 mirtkAttributeMacro(Array<double>, Values);
446 _Values(nvalues, numeric_limits<double>::quiet_NaN())
457 return static_cast<int>(_Values.size());
463 for (
size_t i = 0; i < _Values.size(); ++i) {
475 double Get(
int i = 0)
const 491 double mind,
double minh,
double maxd,
492 const DistanceFunction &distance,
double offset = .0,
493 double tol = 1e-3) = 0;
505 double minh,
double maxd,
506 const DistanceFunction &distance,
double offset = .0,
509 const double mind = ImplicitSurfaceUtils::Evaluate(distance, p, offset);
510 if (abs(mind) < tol) {
512 }
else if (abs(mind) >= maxd) {
515 this->
Evaluate(p, dirs, mind, minh, maxd, distance, offset, tol);
526 double mind,
double minh,
double maxw,
527 const DistanceFunction &distance,
double offset = .0,
530 double e[3], w = maxw;
531 for (
int i = 0; i < dirs.
Size(); ++i) {
533 w = min(w, Width(p, e, mind, minh, maxw, distance, offset, tol));
545 double mind,
double minh,
double maxw,
546 const DistanceFunction &distance,
double offset = .0,
550 for (
int i = 0; i < dirs.
Size(); ++i) {
552 w = max(w, Width(p, e, mind, minh, maxw, distance, offset, tol));
566 double mind,
double minh,
double maxw,
567 const DistanceFunction &distance,
double offset = .0,
570 double e[3], w, wmin = maxw, wmax = .0;
571 for (
int i = 0; i < dirs.
Size(); ++i) {
573 w = Width(p, e, mind, minh, maxw, distance, offset, tol);
574 if (w < wmin) wmin = w;
575 if (w > wmax) wmax = w;
601 double mind,
double minh,
double maxw,
602 const DistanceFunction &distance,
double offset = .0,
606 for (
int i = 0; i < dirs.
Size(); ++i) {
608 w += Width(p, e, mind, minh, maxw, distance, offset, tol);
620 double mind,
double minh,
double maxw,
621 const DistanceFunction &distance,
double offset = .0,
625 Array<double> w(dirs.
Size());
626 for (
int i = 0; i < dirs.
Size(); ++i) {
628 w[i] = Width(p, e, mind, minh, maxw, distance, offset, tol);
630 sort(w.begin(), w.end());
631 Set(0, w[w.size() / 2]);
656 template <
class DistanceMeasurement>
658 double mind,
double minh,
double maxh,
659 const DistanceFunction &distance,
double offset = .0,
663 d.
Evaluate(p, dirs, mind, minh, maxh, distance, offset, tol);
682 template <
class DistanceMeasurement>
684 double minh,
double maxh,
685 const DistanceFunction &distance,
double offset = .0,
689 d.
Evaluate(p, dirs, minh, maxh, distance, offset, tol);
712 template <
class DistanceMeasurement>
714 double mind,
double minh,
double maxh,
715 const DistanceFunction &distance,
double offset = .0,
716 double tol = 1e-3,
int ndirs = 20)
720 d.
Evaluate(p, dirs, mind, minh, maxh, distance, offset, tol);
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)
751 EvaluateSpherical(d, p, n, mind, minh, maxh, distance, offset, tol, ndirs);
771 template <
class DistanceMeasurement>
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)
780 d.
Set(numeric_limits<double>::quiet_NaN());
783 const int nbetas = (beta == .0 ? 1 : 3);
794 const double sqrt2 = sqrt(2.0);
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);
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;
817 d.
Evaluate(p, dirs, mind, minh, maxh, distance, offset, tol);
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)
844 EvaluateTangential(d, p, n, mind, minh, maxh, distance, offset, tol, ndirs, beta);
863 template <
class DistanceMeasurement>
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)
870 const double mind =
Evaluate(distance, p, offset);
871 if (abs(mind) >= maxh) {
877 }
else if (Distance(p, n, mind, minh, 2.0 * tol + tol, distance, offset, .5 * tol) <= tol) {
880 EvaluateTangential(d, p, n, mind, minh, maxh, distance, offset, tol, ndirs, beta);
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)
908 EvaluateTangential(d, p, n, minh, maxh, distance, offset, tol, ndirs, beta);
921 template <
class DistanceMeasurement>
922 DistanceImage
Evaluate(
double maxh,
const DistanceImage &dmap,
923 double offset = .0,
double tol = 1e-3,
int ndirs = 20)
926 const double minh = .1 * ds;
934 DistanceFunction distance;
935 distance.
Input(&dmap);
936 distance.Initialize();
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) {
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.
double GetYSize() const
Returns the size of a voxel in the y-direction.
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)
Determine maximum of the widths measured in multiple directions.
Point & GetPoint(int)
Get n-th point.
Determine maximum of the widths measured in multiple directions.
void Normalize()
Normalize vector to length one.
Array< T >::iterator BinarySearch(Array< T > &values, const T &value)
Find value in sorted Array using binary search.
double GetZSize() const
Returns the size of a voxel in the z-direction.
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)
double GetXSize() const
Returns the size of a voxel in the x-direction.
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 Set(double v)
Set distance value(s)
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.
TVoxel VoxelType
Voxel type.
virtual ~DistanceMeasurement()
Destructor.
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.