UsTK : Ultrasound ToolKit  version 2.0.1 under development (2023-12-07)
usNeedleTrackerSIR2D.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  * Authors:
29  * Pierre Chatelain
30  * Alexandre Krupa
31  *
32  *****************************************************************************/
33 
34 #include <visp3/core/vpImageFilter.h>
35 #include <visp3/ustk_needle_detection/usNeedleTrackerSIR2D.h>
36 
38 {
39  m_needleModel = NULL;
40  m_particles = NULL;
41  m_weights = NULL;
42  m_noise.setSigmaMean(1.0, 0.0);
43  m_noise.seed(42);
44  m_sample = vpUniRand(42);
45  m_sigma.eye(2);
46 }
47 
49 {
50  if (m_needleModel) {
51  delete m_needleModel;
52  m_needleModel = NULL;
53  }
54  if (m_particles) {
55  for (unsigned int i = 0; i < m_nParticles; ++i) {
56  if (m_particles[i]) {
57  delete m_particles[i];
58  m_particles[i] = NULL;
59  }
60  }
61  delete[] m_particles;
62  m_particles = NULL;
63  }
64  if (m_weights) {
65  delete[] m_weights;
66  m_weights = NULL;
67  }
68 }
69 
70 void usNeedleTrackerSIR2D::init(unsigned int dims[2], unsigned int nPoints, unsigned int nParticles,
71  const usPolynomialCurve2D &needle)
72 {
73  m_dims[0] = dims[0];
74  m_dims[1] = dims[1];
75  m_nPoints = nPoints;
76  m_nPointsCurrent = needle.getOrder() + 1;
77  m_nParticles = nParticles;
78  m_needleModel = new usPolynomialCurve2D(needle);
79  m_particles = new usPolynomialCurve2D *[m_nParticles];
80  for (unsigned int i = 0; i < m_nParticles; ++i)
81  m_particles[i] = new usPolynomialCurve2D(needle);
82  m_weights = new double[m_nParticles];
83  for (unsigned int i = 0; i < m_nParticles; ++i)
84  m_weights[i] = 1.0 / m_nParticles;
85  m_lengthThreshold = 50.0;
86 }
87 
88 void usNeedleTrackerSIR2D::init(const vpImage<unsigned char> &I, unsigned int nPoints, unsigned int nParticles,
89  const usPolynomialCurve2D &needle)
90 {
91  unsigned int dims[2];
92  dims[0] = I.getHeight();
93  dims[1] = I.getWidth();
94  m_fgMean = 0.0;
95 
96  vpColVector point;
97  unsigned int c = 0;
98  double length = needle.getLength();
99  for (double t = 0; t <= 1.0; t += 1.0 / length) {
100  point = needle.getPoint(t);
101  unsigned int x = vpMath::round(point[0]);
102  unsigned int y = vpMath::round(point[1]);
103  if ((3 <= x) && (x < dims[0] - 3) && (3 <= y) && (y < dims[1] - 3)) {
104  m_fgMean += vpImageFilter::gaussianFilter(I, x, y);
105  }
106  ++c;
107  }
108  m_fgMean /= c;
109  m_bgMean = I.getSum() / I.getSize();
110 
111  init(dims, nPoints, nParticles, needle);
112 }
113 
115 
117 {
118  if (i >= m_nParticles) {
119  std::cerr << "Error: in usNeedleTrackerSIR2D::getParticle(): "
120  << "Particle index is out of range." << std::endl;
121  exit(EXIT_FAILURE);
122  }
123  return m_particles[i];
124 }
125 
126 double usNeedleTrackerSIR2D::getWeight(unsigned int i)
127 {
128  if (i >= m_nParticles) {
129  std::cerr << "Error: in usNeedleTrackerSIR2D::getWeight(): "
130  << "Particle index is out of range." << std::endl;
131  exit(EXIT_FAILURE);
132  }
133  return m_weights[i];
134 }
135 
136 void usNeedleTrackerSIR2D::setSigma(double s) { m_noise.setSigmaMean(s, 0.0); }
137 
138 void usNeedleTrackerSIR2D::setSigma1(double s) { m_sigma[0][0] = s; }
139 
140 void usNeedleTrackerSIR2D::setSigma2(double s) { m_sigma[1][1] = s; }
141 
142 void usNeedleTrackerSIR2D::run(vpImage<unsigned char> &I, double v)
143 {
144  vpMatrix controlPoints;
145  vpMatrix meanControlPoints;
146  vpColVector direction;
147  vpMatrix S_noise_tip;
148  vpMatrix S_noise_inter;
149  vpColVector noise(2);
150  vpMatrix U(2, 2);
151 
152  // Sample particles
153  for (unsigned int i = 0; i < m_nParticles; ++i) {
154  controlPoints = m_particles[i]->getControlPoints();
155 
156  // Get needle direction
157  direction = m_particles[i]->getTangent(1.0);
158  direction /= direction.frobeniusNorm();
159 
160  // Compute eigen vectors
161  for (unsigned int j = 0; j < 2; ++j)
162  U[j][0] = direction[j];
163  U[0][1] = -direction[1];
164  U[1][1] = direction[0];
165 
166  // Compute noise covariance
167  S_noise_tip = U * m_sigma * U.t();
168  S_noise_inter.eye(2);
169  S_noise_inter *= m_sigma[1][1];
170 
171  // Compute tip velocity vector
172  direction *= v;
173 
174  // Entry point
175  // controlPoints[0][0] += m_noise();
176  // controlPoints[1][0] += m_noise();
177 
178  // Intermediate control points
179  for (unsigned int j = 1; j < m_nPointsCurrent - 1; ++j) {
180 
181  // Generate noise
182  for (unsigned int k = 0; k < 2; ++k)
183  noise[k] = m_noise();
184  noise = S_noise_inter * noise;
185 
186  // Update control points
187  for (unsigned int k = 0; k < 2; ++k)
188  controlPoints[k][j] += direction[k] / (m_nPointsCurrent - 1) + noise[k] / 4.0;
189  }
190 
191  // Tip
192 
193  // Generate noise
194  for (unsigned int k = 0; k < 2; ++k)
195  noise[k] = m_noise();
196  noise = S_noise_tip * noise;
197 
198  // Update control points
199  for (unsigned int k = 0; k < 2; ++k)
200  controlPoints[k][m_nPointsCurrent - 1] += direction[k] + noise[k];
201 
202  m_particles[i]->setControlPoints(controlPoints);
203  }
204 
205  // Compute weights
206  double sumWeights = 0.0;
207  for (unsigned int i = 0; i < m_nParticles; ++i) {
208  m_weights[i] *= computeLikelihood(*(m_particles[i]), I);
209  sumWeights += m_weights[i];
210  }
211 
212  // Normalize the weights and estimate the effective number of particles
213  double sumSquare = 0.0;
214  for (unsigned int i = 0; i < m_nParticles; ++i) {
215  m_weights[i] /= sumWeights;
216  sumSquare += vpMath::sqr(m_weights[i]);
217  }
218 
219  double n_eff = 1.0 / sumSquare;
220  // std::cout << "Neff = " << n_eff << std::endl;
221 
222  // Resampling
223  if (n_eff < m_nParticles / 2.0) {
224  resample();
225  }
226 
227  // Compute mean
228  meanControlPoints.resize(2, m_nPointsCurrent);
229  meanControlPoints = 0.0;
230  for (unsigned int i = 0; i < m_nParticles; ++i) {
231  meanControlPoints += m_weights[i] * m_particles[i]->getControlPoints();
232  }
233 
234  m_needleModel->setControlPoints(meanControlPoints);
235 
236  if ((m_needleModel->getLength()) > m_lengthThreshold && (m_nPoints != m_nPointsCurrent)) {
237  std::cout << "Changing polynomial order from " << m_nPointsCurrent - 1 << " to " << m_nPointsCurrent << std::endl;
238  usPolynomialCurve2D *newModel =
239  new usPolynomialCurve2D(m_needleModel->getNewOrderPolynomialCurve(m_nPointsCurrent));
240  delete m_needleModel;
241  m_needleModel = newModel;
242  for (unsigned int i = 0; i < m_nParticles; ++i) {
243  usPolynomialCurve2D *newModel =
244  new usPolynomialCurve2D(m_particles[i]->getNewOrderPolynomialCurve(m_nPointsCurrent));
245  delete m_particles[i];
246  m_particles[i] = newModel;
247  }
248  m_lengthThreshold *= 2.0;
249  }
250 }
251 
252 double usNeedleTrackerSIR2D::computeLikelihood(const usPolynomialCurve2D &model, const vpImage<unsigned char> &I)
253 {
254  double l = 0.0;
255  vpColVector point;
256  unsigned int c = 0;
257  double length = model.getLength();
258  double intensity;
259 
260  for (double t = 0; t <= 1.0; t += 1.0 / length) {
261  point = model.getPoint(t);
262  unsigned int x = vpMath::round(point[0]);
263  unsigned int y = vpMath::round(point[1]);
264  if ((3 <= x) && (x < m_dims[0] - 3) && (3 <= y) && (y < m_dims[1] - 3)) {
265  ++c;
266  intensity = vpImageFilter::gaussianFilter(I, x, y);
267  l += intensity;
268  }
269  }
270 
271  if (c == 0)
272  return 0.0;
273 
274  l /= c;
275 
276  point = model.getPoint(1.0);
277  unsigned int x = vpMath::round(point[0]);
278  unsigned int y = vpMath::round(point[1]);
279  if ((3 <= x) && (x < m_dims[0] - 3) && (3 <= y) && (y < m_dims[1] - 3)) {
280  intensity = vpImageFilter::gaussianFilter(I, x, y);
281  l += intensity;
282  }
283 
284  point = model.getPoint(1.0 + 1.0 / length);
285  x = vpMath::round(point[0]);
286  y = vpMath::round(point[1]);
287  if ((3 <= x) && (x < m_dims[0] - 3) && (3 <= y) && (y < m_dims[1] - 3)) {
288  intensity = vpImageFilter::gaussianFilter(I, x, y);
289  l -= intensity;
290  }
291 
292  return l;
293 }
294 
296 {
297  // std::cout << "Resampling..." << std::endl;
298  int *idx = new int[m_nParticles];
299  for (unsigned int i = 0; i < m_nParticles; ++i) {
300  double x = m_sample();
301  double sumWeights = 0.0;
302  for (unsigned int j = 0; j < m_nParticles; ++j) {
303  if (x < sumWeights + m_weights[j]) {
304  idx[i] = j;
305  break;
306  }
307  sumWeights += m_weights[j];
308  }
309  }
310  usPolynomialCurve2D **oldParticles = new usPolynomialCurve2D *[m_nParticles];
311  for (unsigned int i = 0; i < m_nParticles; ++i) {
312  oldParticles[i] = new usPolynomialCurve2D(*m_particles[i]);
313  }
314  for (unsigned int i = 0; i < m_nParticles; ++i) {
315  delete m_particles[i];
316  m_particles[i] = new usPolynomialCurve2D(*oldParticles[idx[i]]);
317  }
318  for (unsigned int i = 0; i < m_nParticles; ++i) {
319  delete oldParticles[i];
320  oldParticles[i] = NULL;
321  }
322  delete[] oldParticles;
323  delete[] idx;
324  for (unsigned int i = 0; i < m_nParticles; ++i) {
325  m_weights[i] = 1.0 / m_nParticles;
326  }
327 }
double getWeight(unsigned int i)
usPolynomialCurve2D * getNeedle()
double computeLikelihood(const usPolynomialCurve2D &model, const vpImage< unsigned char > &I)
void init(unsigned int dims[2], unsigned int nPoints, unsigned int nParticles, const usPolynomialCurve2D &needle)
usPolynomialCurve2D * getParticle(unsigned int i)
void run(vpImage< unsigned char > &I, double v)
usPolynomialCurve2D getNewOrderPolynomialCurve(unsigned int order) const
void setControlPoints(const vpMatrix &controlPoints)
double getLength(int nbCountSeg=50) const
vpMatrix getControlPoints() const
vpColVector getTangent(double parameter) const
vpColVector getPoint(double parameter) const
unsigned int getOrder() const