UsTK : Ultrasound ToolKit  version 2.0.1 under development (2024-05-20)
usNeedleModelPolynomial.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/usNeedleModelPolynomial.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_outerDiameter(0.001), m_insideDiameter(0), m_needleYoungModulus(200000000000)
46 {
47  this->init();
48 }
49 
51  : usNeedleModelBaseTip(needle), usPolynomialCurve3D(needle),
52 
53  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 
67 
68  return *this;
69 }
70 
72 {
73  switch (preset) {
75  this->setParametricLength(0.126);
76  this->setOuterDiameter(0.0007);
77  this->setInsideDiameter(0.00048);
78  this->setNeedleYoungModulus(200 * pow(10, 9));
79  break;
80  }
82  this->setParametricLength(0.146);
83  this->setOuterDiameter(0.00048);
84  this->setInsideDiameter(0.0);
85  this->setNeedleYoungModulus(200 * pow(10, 9));
86  break;
87  }
89  this->setParametricLength(0.1);
90  this->setOuterDiameter(0.0005);
91  this->setInsideDiameter(0.0);
92  this->setNeedleYoungModulus(75 * pow(10, 9));
93  break;
94  }
96  this->setParametricLength(0.1);
97  this->setOuterDiameter(0.00046);
98  this->setInsideDiameter(0.0);
99  this->setNeedleYoungModulus(0.5 * pow(10, 9));
100  break;
101  }
103  this->setParametricLength(0.1);
104  this->setOuterDiameter(0.0008);
105  this->setInsideDiameter(0.0);
106  this->setNeedleYoungModulus(75 * pow(10, 9));
107  break;
108  }
110  this->setParametricLength(0.1);
111  this->setOuterDiameter(0.001);
112  this->setInsideDiameter(0.0);
113  this->setNeedleYoungModulus(200 * pow(10, 9));
114  break;
115  }
116  }
117 }
118 
120 {
121  if (diameter > 0)
122  m_outerDiameter = diameter;
123 }
124 
126 
128 {
129  if (diameter >= 0)
130  m_insideDiameter = diameter;
131 }
132 
134 
136 {
137  if (E > 0)
139 }
140 
142 
144 {
145  return m_needleYoungModulus * M_PI / 64 * (pow(m_outerDiameter, 4) - pow(m_insideDiameter, 4));
146 }
147 
149 {
150  this->setBoundaries(0, 0.1);
151  vpMatrix M(3, 4, 0);
152  M[2][1] = 1;
153  this->setPolynomialCoefficients(M);
154 }
155 
156 vpColVector usNeedleModelPolynomial::getNeedlePoint(double l) const
157 {
158  if (l < 0 || l > m_endParameter)
159  throw vpException(vpException::badValue, "usNeedleModelPolynomial::getNeedlePoint: length out of needle");
160 
161  return this->getPoint(l);
162 }
163 
165 {
166  if (l < 0 || l > m_endParameter)
167  throw vpException(vpException::badValue, "usNeedleModelPolynomial::getNeedleDirection: length out of needle");
168 
169  return this->getTangent(l);
170 }
171 
172 double usNeedleModelPolynomial::getDistanceFromPoint(const vpColVector &P, double start, double stop,
173  double threshold) const
174 {
175  if (P.size() != 3)
176  throw vpException(vpException::dimensionError,
177  "usNeedleModelPolynomial::getDistanceFromPoint: invalid point dimension");
178 
179  if (start < 0)
180  start = 0;
181  if (stop == -1 || stop > m_endParameter)
182  stop = m_endParameter;
183  if (stop < 0)
184  stop = 0;
185  if (start > stop)
186  throw vpException(vpException::badValue,
187  "usNeedleModelPolynomial::getDistanceFromPoint: invalid start-stop parameters");
188 
189  double middle = (start + stop) / 2;
190  while ((stop - start) > threshold) {
191  double d0 = (this->getNeedlePoint(start) - P).frobeniusNorm();
192  double d1 = (this->getNeedlePoint(middle) - P).frobeniusNorm();
193  double d2 = (this->getNeedlePoint(stop) - P).frobeniusNorm();
194 
195  if (d0 <= d1 && d0 < d2)
196  stop = middle;
197  else if (d2 < d0 && d2 <= d1)
198  start = middle;
199  else {
200  start = (start + middle) / 2;
201  stop = (middle + stop) / 2;
202  }
203  middle = (start + stop) / 2;
204  }
205 
206  double l = (this->getNeedlePoint(middle) - P).frobeniusNorm();
207 
208  return l;
209 }
210 
212 {
213  vpColVector Ls_pow(2 * m_order - 2);
214  Ls_pow[0] = 1;
215  vpColVector Le_pow(2 * m_order - 2);
216  Le_pow[0] = 1;
217 
218  for (unsigned int i = 1; i < 2 * m_order - 2; i++) {
219  Ls_pow[i] = m_startParameter * Ls_pow[i - 1];
220  Le_pow[i] = m_endParameter * Le_pow[i - 1];
221  }
222 
223  vpMatrix dotProd = m_polynomialCoefficients.t() * m_polynomialCoefficients;
224 
225  double E = 0;
226 
227  for (unsigned int i = 2; i <= m_order; i++) {
228  for (unsigned int j = 2; j <= m_order; j++) {
229  E += i * (i - 1) * j * (j - 1) / (i + j - 3) * dotProd[i][j] * (Le_pow[i + j - 3] - Ls_pow[i + j - 3]);
230  }
231  }
232 
233  E *= 0.5 * this->getEI();
234 
235  return E;
236 }
237 
239 {
240  vpColVector d2X_dl2 = this->getDerivative(0, 2);
241 
242  double EI = this->getEI();
243 
244  vpColVector Moment = EI * vpColVector::crossProd(this->getDerivative(0, 1), d2X_dl2);
245 
246  vpColVector d3X_dl3 = this->getDerivative(0, 3);
247 
248  vpColVector Force = EI * (d3X_dl3 - vpColVector::dotProd(d3X_dl3, this->getStartTangent()) * this->getStartTangent());
249 
250  vpColVector Torsor(6);
251  Torsor[0] = Force[0];
252  Torsor[1] = Force[1];
253  Torsor[2] = Force[2];
254  Torsor[3] = Moment[0];
255  Torsor[4] = Moment[1];
256  Torsor[5] = Moment[2];
257 
258  return Torsor;
259 }
260 
261 double usNeedleModelPolynomial::getCurvatureFromNeedleShape(double start, double end, vpColVector &center3D,
262  vpColVector &direction3D) const
263 {
264  if (start < 0)
265  start = 0;
266  if (start > m_endParameter)
267  start = m_endParameter;
268  if (end < 0)
269  end = 0;
270  if (end > m_endParameter)
271  end = m_endParameter;
272  if (start > end) {
273  double tmp = start;
274  start = end;
275  end = tmp;
276  }
277 
278  int subSegmentsNumber = 100;
279  int nbPoints = subSegmentsNumber + 1;
280 
281  if (nbPoints < 3) {
282  return 0;
283  }
284 
285  // Create data matrix with centered vectors
286  vpMatrix M(nbPoints, 3);
287  vpRowVector mean(3, 0);
288 
289  double step = this->getParametricLength() / subSegmentsNumber;
290  for (int i = 0; i < nbPoints; i++)
291  M.insert(this->getPoint(m_startParameter + i * step).t(), i, 0);
292 
293  for (int i = 0; i < nbPoints; i++)
294  mean += M.getRow(i);
295  mean /= nbPoints;
296 
297  for (int i = 0; i < nbPoints; i++) {
298  M[i][0] -= mean[0];
299  M[i][1] -= mean[1];
300  M[i][2] -= mean[2];
301  }
302 
303  // Reduction to two principal components using singular value decomposition
304  vpMatrix U(M);
305  vpColVector w;
306  vpMatrix V;
307  U.svd(w, V);
308 
309  vpMatrix S;
310  S.diag(w);
311 
312  U.resize(nbPoints, 2, false);
313 
314  S.resize(2, 2, false);
315 
316  vpMatrix P = U * S;
317 
318  // 2D nonlinear least square fitting (Coope93)
319  vpColVector d(nbPoints);
320  for (int i = 0; i < nbPoints; i++) {
321  d[i] = pow(P.t().getCol(i).frobeniusNorm(), 2);
322  }
323 
324  vpColVector x(nbPoints, 1);
325  vpMatrix B(nbPoints, 3);
326  B.insert(P, 0, 0);
327  B.insert(x, 0, 2);
328 
329  vpColVector y = B.pseudoInverse(0) * d;
330 
331  vpColVector center(2);
332  center[0] = y[0] / 2;
333  center[1] = y[1] / 2;
334 
335  double r = sqrt(y[2] + pow(center.frobeniusNorm(), 2));
336 
337  // Check validity
338 
339  vpColVector cp = P.getRow(0).t() - center;
340  for (int i = 1; i < nbPoints; i++) {
341  double dot = vpColVector::dotProd(cp, P.getRow(i).t() - center);
342  if (dot < 0)
343  return 0;
344  }
345 
346  if (direction3D.size() == 3) {
347  direction3D = V.getCol(2);
348  } else if (direction3D.size() == 4) {
349  direction3D.insert(0, V.getCol(2));
350  direction3D[3] = 0;
351  }
352 
353  if (center3D.size() == 3) {
354  V.resize(3, 2, false);
355  center3D = V * center;
356  } else if (center3D.size() == 4) {
357  V.resize(3, 2, false);
358  center3D.insert(0, V * center + mean.t());
359  center3D[3] = 1;
360  }
361 
362  return 1.0 / r;
363 }
364 
365 std::ostream &operator<<(std::ostream &s, const usNeedleModelPolynomial &needle)
366 {
367  s << "usNeedleModelPolynomial\n";
368  s << *((usNeedleModelBaseTip *)(&needle));
369  s << *((usPolynomialCurve3D *)(&needle));
370  s << needle.m_outerDiameter << '\n';
371  s << needle.m_insideDiameter << '\n';
372  s << needle.m_needleYoungModulus << '\n';
373 
374  s.flush();
375  return s;
376 }
377 
378 std::istream &operator>>(std::istream &s, usNeedleModelPolynomial &needle)
379 {
380  std::string c;
381  s >> c;
382  if (c != "usNeedleModelPolynomial") {
383  vpException e(vpException::ioError, "Stream does not contain usNeedleModelPolynomial data");
384  throw e;
385  }
386  s >> (*(usNeedleModelBaseTip *)(&needle));
387  s >> (*(usPolynomialCurve3D *)(&needle));
388  s >> needle.m_outerDiameter;
389  s >> needle.m_insideDiameter;
390  s >> needle.m_needleYoungModulus;
391 
392  return s;
393 }
394 
395 std::ostream &operator<<=(std::ostream &s, const usNeedleModelPolynomial &needle)
396 {
397  s.write("usNeedleModelPolynomial", 24);
398  s <<= *((usNeedleModelBaseTip *)(&needle));
399  s <<= *((usPolynomialCurve3D *)(&needle));
400  s.write((char *)&(needle.m_outerDiameter), sizeof(double));
401  s.write((char *)&(needle.m_insideDiameter), sizeof(double));
402  s.write((char *)&(needle.m_needleYoungModulus), sizeof(double));
403 
404  s.flush();
405  return s;
406 }
407 
408 std::istream &operator>>=(std::istream &s, usNeedleModelPolynomial &needle)
409 {
410  char c[24];
411  s.read(c, 24);
412  if (strcmp(c, "usNeedleModelPolynomial")) {
413  vpException e(vpException::ioError, "Stream does not contain usNeedleModelPolynomial data");
414  throw e;
415  }
416  s >>= *((usNeedleModelBaseTip *)(&needle));
417  s >>= *((usPolynomialCurve3D *)(&needle));
418  s.read((char *)&(needle.m_outerDiameter), sizeof(double));
419  s.read((char *)&(needle.m_insideDiameter), sizeof(double));
420  s.read((char *)&(needle.m_needleYoungModulus), sizeof(double));
421 
422  return s;
423 }
virtual usNeedleModelBaseTip & operator=(const usNeedleModelBaseTip &needle)
vpColVector getBaseStaticTorsor() const
Force at base.
double getBendingEnergy() const
Needle bending.
const usNeedleModelPolynomial & operator=(const usNeedleModelPolynomial &needle)
double m_outerDiameter
Needle parameters.
usNeedleModelPolynomial()
Constructors, destructor.
void setOuterDiameter(double diameter)
Parameters setters and getters.
vpColVector getNeedlePoint(double l) const
Measure model information.
double getDistanceFromPoint(const vpColVector &P, double start=0, double stop=-1, double threshold=1e-5) const
void init()
Spline definition.
void setInsideDiameter(double diameter)
void loadPreset(const NeedlePreset preset)
Parameters saving and loading.
double getCurvatureFromNeedleShape(double start, double end, vpColVector &center3D, vpColVector &direction3D) const
Curvature.
vpColVector getNeedleDirection(double l) const
vpColVector getDerivative(double parameter, unsigned int order) const
vpColVector getStartTangent() const
vpColVector getTangent(double parameter) const
vpColVector getPoint(double parameter) const
void setParametricLength(double length)
void setPolynomialCoefficients(const vpMatrix &polynomialCoefficients)
double getParametricLength() const
void setBoundaries(double startParameter, double endParamter)