UsTK : Ultrasound ToolKit  version 2.0.1 under development (2024-05-17)
usNeedleModelSpline.cpp
1 /****************************************************************************
2  *
3  * This file is part of the ustk software.
4  * Copyright (C) 2016 - 2017 by Inria. All rights reserved.
5  *
6  * This software is free software; you can redistribute it and/or
7  * modify it under the terms of the GNU General Public License
8  * ("GPL") version 2 as published by the Free Software Foundation.
9  * See the file LICENSE.txt at the root directory of this source
10  * distribution for additional information about the GNU GPL.
11  *
12  * For using ustk with software that can not be combined with the GNU
13  * GPL, please contact Inria about acquiring a ViSP Professional
14  * Edition License.
15  *
16  * This software was developed at:
17  * Inria Rennes - Bretagne Atlantique
18  * Campus Universitaire de Beaulieu
19  * 35042 Rennes Cedex
20  * France
21  *
22  * If you have questions regarding the use of this file, please contact
23  * Inria at ustk@inria.fr
24  *
25  * This file is provided AS IS with NO WARRANTY OF ANY KIND, INCLUDING THE
26  * WARRANTY OF DESIGN, MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE.
27  *
28  * Author:
29  * Jason Chevrie
30  *
31  *****************************************************************************/
32 
33 #include <visp3/ustk_needle_modeling/usNeedleModelSpline.h>
34 
35 #include <iomanip>
36 
37 #include <visp3/core/vpException.h>
38 #include <visp3/core/vpMatrix.h>
39 #include <visp3/core/vpRowVector.h>
40 #include <visp3/core/vpTime.h>
41 
44 
45  m_length(0.1), m_outerDiameter(0.001), m_insideDiameter(0), m_needleYoungModulus(200000000000)
46 {
47  this->init();
48 }
49 
51  : usNeedleModelBaseTip(needle), usBSpline3D(needle),
52 
53  m_length(needle.m_length), m_outerDiameter(needle.m_outerDiameter), m_insideDiameter(needle.m_insideDiameter),
54  m_needleYoungModulus(needle.m_needleYoungModulus)
55 {
56 }
57 
59 
61 {
62  this->usNeedleModelBaseTip::operator=(needle);
63  this->usBSpline3D::operator=(needle);
64 
65  m_length = needle.m_length;
69 
70  return *this;
71 }
72 
74 {
75  switch (preset) {
77  this->setFullLength(0.126);
78  this->setOuterDiameter(0.0007);
79  this->setInsideDiameter(0.00048);
80  this->setNeedleYoungModulus(200e9);
81  break;
82  }
84  this->setFullLength(0.146);
85  this->setOuterDiameter(0.00048);
86  this->setInsideDiameter(0.0);
87  this->setNeedleYoungModulus(200e9);
88  break;
89  }
91  this->setFullLength(0.109);
92  this->setOuterDiameter(0.00097);
93  this->setInsideDiameter(0);
94  this->setNeedleYoungModulus(200e9);
95  break;
96  }
98  this->setFullLength(0.1);
99  this->setOuterDiameter(0.0005);
100  this->setInsideDiameter(0.0);
101  this->setNeedleYoungModulus(75e9);
102  break;
103  }
105  this->setFullLength(0.15);
106  this->setOuterDiameter(0.00046);
107  this->setInsideDiameter(0.0);
108  this->setNeedleYoungModulus(50e9);
109  break;
110  }
112  this->setFullLength(0.1);
113  this->setOuterDiameter(0.0008);
114  this->setInsideDiameter(0.0);
115  this->setNeedleYoungModulus(75e9);
116  break;
117  }
119  this->setFullLength(0.1);
120  this->setOuterDiameter(0.001);
121  this->setInsideDiameter(0.0);
122  this->setNeedleYoungModulus(200e9);
123  break;
124  }
126  this->setFullLength(0.1);
127  this->setOuterDiameter(0.002);
128  this->setInsideDiameter(0.0);
129  this->setNeedleYoungModulus(8062500000); // 0 -> 0.5mm = Nitinol (75GPa) + 0.5mm -> 1mm = PEEK (3.6GPa)
130  break;
131  }
133  this->setFullLength(0.127);
134  this->setOuterDiameter(0.00075);
135  this->setInsideDiameter(0.);
136  this->setNeedleYoungModulus(200e9);
137  break;
138  }
140  this->setFullLength(0.164);
141  this->setOuterDiameter(0.00055);
142  this->setInsideDiameter(0.0005);
143  this->setNeedleYoungModulus(200e9);
144  break;
145  }
147  this->setFullLength(0.148);
148  this->setOuterDiameter(0.00105);
149  this->setInsideDiameter(0.000838);
150  this->setNeedleYoungModulus(200e9);
151  break;
152  }
153  }
154 }
155 
157 {
158  if (length >= 0)
159  m_length = length;
160 }
161 
163 
165 {
166  if (diameter > 0)
167  m_outerDiameter = diameter;
168 }
169 
171 
173 {
174  if (diameter >= 0)
175  m_insideDiameter = diameter;
176 }
177 
179 
181 {
182  if (E > 0)
184 }
185 
187 
189 {
190  return m_needleYoungModulus * M_PI / 64 * (pow(m_outerDiameter, 4) - pow(m_insideDiameter, 4));
191 }
192 
194 {
195  m_spline.clear();
196  usPolynomialCurve3D poly;
197  poly.setBoundaries(0, m_length);
198  vpMatrix M(3, 4, 0);
199  M[2][1] = 1;
201  m_spline.push_back(poly);
202 }
203 
204 vpColVector usNeedleModelSpline::getNeedlePoint(double l) const
205 {
206  if (l < 0 || l > m_length)
207  throw vpException(vpException::badValue, "usNeedleModelSpline::getNeedlePoint: length out of needle");
208 
209  return this->getPoint(l);
210 }
211 
212 vpColVector usNeedleModelSpline::getNeedleDirection(double l) const
213 {
214  if (l < 0 || l > m_length)
215  throw vpException(vpException::badValue, "usNeedleModelSpline::getNeedleDirection: length out of needle");
216 
217  return this->getTangent(l);
218 }
219 
220 double usNeedleModelSpline::getDistanceFromPoint(const vpColVector &P, double start, double stop,
221  double threshold) const
222 {
223  if (P.size() != 3)
224  throw vpException(vpException::dimensionError,
225  "usNeedleModelSpline::getDistanceFromPoint: invalid point dimension");
226 
227  if (start < 0)
228  start = 0;
229  if (stop == -1 || stop > m_length)
230  stop = m_length;
231  if (stop < 0.)
232  stop = 0.;
233  if (start > stop)
234  throw vpException(vpException::badValue,
235  "usNeedleModelSpline::getDistanceFromPoint: invalid start-stop parameters");
236 
237  double middle = (start + stop) / 2;
238  while ((stop - start) > threshold) {
239  double d0 = (this->getNeedlePoint(start) - P).frobeniusNorm();
240  double d1 = (this->getNeedlePoint(middle) - P).frobeniusNorm();
241  double d2 = (this->getNeedlePoint(stop) - P).frobeniusNorm();
242 
243  if (d0 <= d1 && d0 < d2)
244  stop = middle;
245  else if (d2 < d0 && d2 <= d1)
246  start = middle;
247  else {
248  start = (start + middle) / 2;
249  stop = (middle + stop) / 2;
250  }
251  middle = (start + stop) / 2;
252  }
253 
254  double l = (this->getNeedlePoint(middle) - P).frobeniusNorm();
255 
256  return l;
257 }
258 
260 {
261  double E = 0;
262 
263  if (m_spline.front().getOrder() < 2) {
264  for (unsigned int n = 0; n < m_spline.size() - 1; n++) {
265  const usPolynomialCurve3D &poly = m_spline.at(n);
266  const usPolynomialCurve3D &poly2 = m_spline.at(n + 1);
267 
268  vpColVector dX_dl = poly.getDerivative(poly.getStartParameter(), 1);
269  vpColVector dX_dl_2 = poly2.getDerivative(poly2.getStartParameter(), 1);
270 
271  double norm = dX_dl.frobeniusNorm();
272  double curvature = vpColVector::crossProd(dX_dl, dX_dl_2).frobeniusNorm() / pow(norm, 3);
273 
274  E += pow(curvature, 2);
275  }
276  } else {
277  for (unsigned int n = 0; n < m_spline.size(); n++) {
278  const usPolynomialCurve3D &poly = m_spline.at(n);
279 
280  int m = poly.getOrder();
281 
282  double Ls = poly.getStartParameter();
283  double Le = poly.getEndParameter();
284 
285  vpColVector Ls_pow(2 * m - 2);
286  Ls_pow[0] = 1;
287  vpColVector Le_pow(2 * m - 2);
288  Le_pow[0] = 1;
289 
290  for (int i = 1; i < 2 * m - 2; i++) {
291  Ls_pow[i] = Ls * Ls_pow[i - 1];
292  Le_pow[i] = Le * Le_pow[i - 1];
293  }
294 
295  vpMatrix dotProd = poly.getPolynomialCoefficients().AtA();
296 
297  for (int i = 2; i <= m; i++) {
298  for (int j = 2; j <= m; j++) {
299  E += i * (i - 1) * j * (j - 1) / (i + j - 3) * dotProd[i][j] * (Le_pow[i + j - 3] - Ls_pow[i + j - 3]);
300  }
301  }
302 
303  /* Trapeze integration method
304  int subSegmentsNumber = 100;
305  double l = poly.getStartParameter();
306  double dl = poly.getParametricLength()/subSegmentsNumber;
307  double dE = pow(poly.getCurvature(l),2) / 2;
308  l += dl;
309  for(int k=1 ; k<subSegmentsNumber ; k++)
310  {
311  dE += pow(poly.getCurvature(l),2);
312  l += dl;
313  }
314  dE += pow(poly.getCurvature(l),2) / 2;
315  E += dl*dE;*/
316  }
317  }
318 
319  E *= 0.5 * this->getEI();
320 
321  return E;
322 }
323 
325 {
326  const usPolynomialCurve3D &poly = m_spline.front();
327  vpColVector d2X_dl2 = poly.getDerivative(0, 2);
328 
329  double EI = this->getEI();
330 
331  vpColVector Moment = -EI * vpColVector::crossProd(poly.getStartTangent(), d2X_dl2);
332 
333  vpColVector d3X_dl3 = poly.getDerivative(0, 3);
334 
335  vpColVector Force = EI * (d3X_dl3 - vpColVector::dotProd(d3X_dl3, poly.getStartTangent()) * poly.getStartTangent());
336 
337  vpColVector Torsor(6);
338  Torsor[0] = Force[0];
339  Torsor[1] = Force[1];
340  Torsor[2] = Force[2];
341  Torsor[3] = Moment[0];
342  Torsor[4] = Moment[1];
343  Torsor[5] = Moment[2];
344 
345  return Torsor;
346 }
347 
348 double usNeedleModelSpline::getCurvatureFromNeedleShape(double start, double end, vpColVector &center3D,
349  vpColVector &direction3D) const
350 {
351  if (start < 0)
352  start = 0;
353  if (start > m_length)
354  start = m_length;
355  if (end < 0)
356  end = 0;
357  if (end > m_length)
358  end = m_length;
359  if (start > end) {
360  double tmp = start;
361  start = end;
362  end = tmp;
363  }
364 
365  unsigned int seg = 0;
366  double L = 0;
367 
368  while (L <= start && seg < m_spline.size()) {
369  L += m_spline.at(seg).getParametricLength();
370  seg++;
371  }
372  int nStart = seg - 1;
373 
374  while (L <= end && seg < m_spline.size()) {
375  L += m_spline.at(seg).getParametricLength();
376  seg++;
377  }
378 
379  int nEnd = seg - 2;
380 
381  int nbPoints = nEnd - nStart + 1;
382 
383  if (nbPoints < 3) {
384  return 0;
385  }
386 
387  // Create data matrix with centered vectors
388  vpMatrix M(nbPoints, 3);
389  vpRowVector mean(3, 0);
390 
391  for (int i = 0; i < nbPoints; i++)
392  M.insert(m_spline.at(nStart + i).getPoint(m_spline.at(nStart + i).getEndParameter()).t(), i, 0);
393 
394  for (int i = 0; i < nbPoints; i++)
395  mean += M.getRow(i);
396  mean /= nbPoints;
397 
398  for (int i = 0; i < nbPoints; i++) {
399  M[i][0] -= mean[0];
400  M[i][1] -= mean[1];
401  M[i][2] -= mean[2];
402  }
403 
404  // Reduction to two principal components using singular value decomposition
405  vpMatrix U(M);
406  vpColVector w;
407  vpMatrix V;
408  U.svd(w, V);
409 
410  vpMatrix S;
411  S.diag(w);
412 
413  U.resize(nbPoints, 2, false);
414 
415  S.resize(2, 2, false);
416 
417  vpMatrix P = U * S;
418 
419  // 2D nonlinear least square fitting (Coope93)
420  vpColVector d(nbPoints);
421  for (int i = 0; i < nbPoints; i++) {
422  d[i] = pow(P.t().getCol(i).frobeniusNorm(), 2);
423  }
424 
425  vpColVector x(nbPoints, 1);
426  vpMatrix B(nbPoints, 3);
427  B.insert(P, 0, 0);
428  B.insert(x, 0, 2);
429 
430  vpColVector y = B.pseudoInverse(0) * d;
431 
432  vpColVector center(2);
433  center[0] = y[0] / 2;
434  center[1] = y[1] / 2;
435 
436  double r = sqrt(y[2] + pow(center.frobeniusNorm(), 2));
437 
438  // Check validity
439 
440  vpColVector cp = P.getRow(0).t() - center;
441  for (int i = 1; i < nbPoints; i++) {
442  double dot = vpColVector::dotProd(cp, P.getRow(i).t() - center);
443  if (dot < 0)
444  return 0;
445  }
446 
447  if (direction3D.size() == 3) {
448  direction3D = V.getCol(2);
449  } else if (direction3D.size() == 4) {
450  direction3D.insert(0, V.getCol(2));
451  direction3D[3] = 0;
452  }
453 
454  if (center3D.size() == 3) {
455  V.resize(3, 2, false);
456  center3D = V * center;
457  } else if (center3D.size() == 4) {
458  V.resize(3, 2, false);
459  center3D.insert(0, V * center + mean.t());
460  center3D[3] = 1;
461  }
462 
463  return 1.0 / r;
464  /*
465  usPolynomialCurve3D seg(m_spline.back());
466 
467  vpColVector dX_dl = seg.getDerivative(seg.getParametricLength(),2);
468  if(dX_dl.frobeniusNorm()<std::numeric_limits<double>::epsilon()) return 0;
469 
470  vpColVector d2X_dl2 = seg.getDerivative(seg.getParametricLength(),2);
471 
472  double k = vpColVector::crossProd(dX_dl, d2X_dl2).frobeniusNorm() / pow(dX_dl.frobeniusNorm(),3);
473 
474  center3D = seg.getEndPoint() + 1/k * ( d2X_dl2 - 1/pow(dX_dl.frobeniusNorm(),2) * vpColVector::dotProd(dX_dl,
475  d2X_dl2)*dX_dl);
476  direction3D = vpColVector::crossProd(dX_dl, d2X_dl2).normalize();
477  return k;*/
478 }
479 
481 {
482  std::cout << "Needle " << this << ": " << m_spline.size() + 1 << " NeedlePoints: \n";
483  for (unsigned int i = 0; i < m_spline.size(); i++) {
484  std::cout << "\t Point " << i << ":\n";
485  vpColVector p = m_spline.at(i).getPoint(m_spline.at(i).getStartParameter());
486  for (int j = 0; j < 3; j++) {
487  std::cout << "\t\t " << p[j] << "\n";
488  }
489  }
490  std::cout << "\t Point " << m_spline.size() << ":\n";
491  vpColVector p = m_spline.back().getPoint(m_spline.back().getEndParameter());
492  for (int j = 0; j < 3; j++) {
493  std::cout << "\t\t " << p[j] << "\n";
494  }
495  std::cout << std::endl;
496 }
497 
499 {
500  std::cout << "Needle " << this << ": " << m_spline.size() << " NeedleDirections: \n";
501  for (unsigned int i = 0; i < m_spline.size(); i++) {
502  std::cout << "\t Direction " << i << ":\n";
503  vpColVector d = m_spline.at(i).getTangent(m_spline.at(i).getStartParameter());
504  for (int j = 0; j < 3; j++) {
505  std::cout << "\t\t " << d[j] << "\n";
506  }
507  }
508  std::cout << "\t Direction " << m_spline.size() << ":\n";
509  vpColVector d = m_spline.back().getTangent(m_spline.back().getEndParameter());
510  for (int j = 0; j < 3; j++) {
511  std::cout << "\t\t " << d[j] << "\n";
512  }
513  std::cout << std::endl;
514 }
515 
517 {
518  std::cout << "Needle " << this << ": " << m_spline.size() << " NeedleSegmentCoef: \n";
519  for (unsigned int i = 0; i < m_spline.size(); i++) {
520  std::cout << "\t Segment " << i << ":\n";
521  vpMatrix m = m_spline.at(i).getPolynomialCoefficients();
522  for (int j = 0; j < 3; j++) {
523  std::cout << "\t\t " << std::setw(15) << m[j][0] << " " << std::setw(15) << m[j][1] << " " << std::setw(15)
524  << m[j][2] << " " << std::setw(15) << m[j][3] << "\n";
525  }
526  }
527  std::cout << std::endl;
528 }
529 
531 {
532  std::cout << "Needle " << this << ": " << m_spline.size() << " NeedleSegmentLength: \n";
533  for (unsigned int i = 0; i < m_spline.size(); i++) {
534  std::cout << "\t Segment " << i << ": " << m_spline.at(i).getParametricLength() << ":\n";
535  }
536  std::cout << std::endl;
537 }
538 
539 std::ostream &operator<<(std::ostream &s, const usNeedleModelSpline &needle)
540 {
541  s << *((usNeedleModelBaseTip *)(&needle));
542 
543  s << "usNeedleModelSpline\n";
544  s << needle.m_length << '\n';
545  s << needle.m_outerDiameter << '\n';
546  s << needle.m_insideDiameter << '\n';
547  s << needle.m_needleYoungModulus << '\n';
548 
549  int n = needle.m_spline.size();
550  s << n << '\n';
551  for (int i = 0; i < n; i++)
552  s << needle.m_spline.at(i);
553 
554  s.flush();
555  return s;
556 }
557 
558 std::istream &operator>>(std::istream &s, usNeedleModelSpline &needle)
559 {
560  s >> (*(usNeedleModelBaseTip *)(&needle));
561 
562  std::string c;
563  s >> c;
564  if (c != "usNeedleModelSpline") {
565  vpException e(vpException::ioError, "Stream does not contain usNeedleModelSpline data");
566  throw e;
567  }
568  s >> needle.m_length;
569  s >> needle.m_outerDiameter;
570  s >> needle.m_insideDiameter;
571  s >> needle.m_needleYoungModulus;
572 
573  int n = 0;
574  s >> n;
575  needle.m_spline.clear();
576  needle.m_spline.resize(n);
577  for (int i = 0; i < n; i++)
578  s >> needle.m_spline.at(i);
579 
580  return s;
581 }
582 
583 std::ostream &operator<<=(std::ostream &s, const usNeedleModelSpline &needle)
584 {
585  s <<= *((usNeedleModelBaseTip *)(&needle));
586 
587  s.write("usNeedleModelSpline", 20);
588  s.write((char *)&(needle.m_length), sizeof(double));
589  s.write((char *)&(needle.m_outerDiameter), sizeof(double));
590  s.write((char *)&(needle.m_insideDiameter), sizeof(double));
591  s.write((char *)&(needle.m_needleYoungModulus), sizeof(double));
592 
593  int n = needle.m_spline.size();
594  s.write((char *)&n, sizeof(int));
595  for (int i = 0; i < n; i++)
596  s <<= needle.m_spline.at(i);
597 
598  s.flush();
599  return s;
600 }
601 
602 std::istream &operator>>=(std::istream &s, usNeedleModelSpline &needle)
603 {
604  s >>= *((usNeedleModelBaseTip *)(&needle));
605 
606  char c[20];
607  s.read(c, 20);
608  if (strcmp(c, "usNeedleModelSpline")) {
609  vpException e(vpException::ioError, "Stream does not contain usNeedleModelSpline data");
610  throw e;
611  }
612  s.read((char *)&(needle.m_length), sizeof(double));
613  s.read((char *)&(needle.m_outerDiameter), sizeof(double));
614  s.read((char *)&(needle.m_insideDiameter), sizeof(double));
615  s.read((char *)&(needle.m_needleYoungModulus), sizeof(double));
616 
617  int n = 0;
618  s.read((char *)&n, sizeof(int));
619  needle.m_spline.clear();
620  needle.m_spline.resize(n);
621  for (int i = 0; i < n; i++)
622  s >>= needle.m_spline.at(i);
623 
624  return s;
625 }
std::vector< usPolynomialCurve3D > m_spline
Polynomials container.
Definition: usBSpline3D.h:48
vpColVector getTangent(double param) const
const usBSpline3D & operator=(const usBSpline3D &spline)
Definition: usBSpline3D.cpp:51
vpColVector getPoint(double param) const
Measure curve information.
virtual usNeedleModelBaseTip & operator=(const usNeedleModelBaseTip &needle)
double getDistanceFromPoint(const vpColVector &P, double start=0, double stop=-1, double threshold=1e-5) const
void setFullLength(double length)
Parameters setters and getters.
vpColVector getBaseStaticTorsor() const
Force at base.
vpColVector getNeedleDirection(double l) const
const usNeedleModelSpline & operator=(const usNeedleModelSpline &needle)
double m_length
Needle parameters.
double getInsideDiameter() const
void setNeedleYoungModulus(double E)
void showNeedlePoints() const
Display.
double getCurvatureFromNeedleShape(double start, double end, vpColVector &center3D, vpColVector &direction3D) const
Curvature.
vpColVector getNeedlePoint(double l) const
Measure model information.
void showNeedleSegmentCoef() const
void init()
Spline definition.
double getOuterDiameter() const
void setInsideDiameter(double diameter)
usNeedleModelSpline()
Constructors, destructor.
double getNeedleYoungModulus() const
void setOuterDiameter(double diameter)
void showNeedleSegmentLength() const
double getBendingEnergy() const
Needle bending.
void loadPreset(const NeedlePreset preset)
Parameters saving and loading.
vpColVector getDerivative(double parameter, unsigned int order) const
vpColVector getStartTangent() const
unsigned int getOrder() const
double getStartParameter() const
void setPolynomialCoefficients(const vpMatrix &polynomialCoefficients)
vpMatrix getPolynomialCoefficients() const
double getEndParameter() const
void setBoundaries(double startParameter, double endParamter)