UsTK : Ultrasound ToolKit  version 2.0.1 under development (2025-03-14)
tutorial-needleDetection2D.cpp
1 /****************************************************************************
2  *
3  * This file is part of the UsNeedleDetection software.
4  * Copyright (C) 2013 - 2016 by Inria. All rights reserved.
5  *
6  * This program is free software: you can redistribute it and/or modify
7  * it under the terms of the GNU General Public License ("GPL") as
8  * published by the Free Software Foundation, either version 3 of the
9  * License, or (at your option) any later version.
10  * See the file COPYING at the root directory of this source
11  * distribution for additional information about the GNU GPL.
12  *
13  * This software was developed at:
14  * INRIA Rennes - Bretagne Atlantique
15  * Campus Universitaire de Beaulieu
16  * 35042 Rennes Cedex
17  * France
18  * http://www.irisa.fr/lagadic
19  *
20  * If you have questions regarding the use of this file, please contact the
21  * authors at Alexandre.Krupa@inria.fr
22  *
23  * This file is provided AS IS with NO WARRANTY OF ANY KIND, INCLUDING THE
24  * WARRANTY OF DESIGN, MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE.
25  *
26  * Authors:
27  * Pierre Chatelain
28  * Alexandre Krupa
29  *
30  *****************************************************************************/
31 
32 #include <iostream>
33 #include <sstream>
34 #include <string>
35 
36 // visp
37 #include <visp3/core/vpConfig.h>
38 
39 #ifdef VISP_HAVE_XML2
40 #include <visp3/core/vpIoTools.h>
41 #include <visp3/core/vpMatrix.h>
42 #include <visp3/gui/vpDisplayD3D.h>
43 #include <visp3/gui/vpDisplayGDI.h>
44 #include <visp3/gui/vpDisplayGTK.h>
45 #include <visp3/gui/vpDisplayOpenCV.h>
46 #include <visp3/gui/vpDisplayX.h>
47 #include <visp3/gui/vpPlot.h>
48 #include <visp3/io/vpImageIo.h>
49 #include <visp3/io/vpParseArgv.h>
50 
51 // ustk
52 #include <visp3/ustk_core/us.h>
53 #include <visp3/ustk_core/usSequenceReader.h>
54 #include <visp3/ustk_needle_detection/usNeedleTrackerSIR2D.h>
55 
56 using namespace std;
57 
58 /* -------------------------------------------------------------------------- */
59 /* COMMAND LINE OPTIONS */
60 /* -------------------------------------------------------------------------- */
61 
62 // List of allowed command line options
63 #define GETOPTARGS "cdo:h"
64 
65 void usage(const char *name, const char *badparam, const std::string &opath, const std::string &user);
66 bool getOptions(int argc, const char **argv, std::string &opath, const std::string &user);
67 
78 void usage(const char *name, const char *badparam, const std::string &opath, const std::string &user)
79 {
80  fprintf(stdout, "\n\
81  Write and read ultrasound sequences in 2d image files, and the associated xml settings file.\n\
82  \n\
83  SYNOPSIS\n\
84  %s [-o <output image path>] [-h]\n",
85  name);
86 
87  fprintf(stdout, "\n\
88  OPTIONS: Default\n\
89  -o <output data path> %s\n\
90  Set data output path.\n\
91  From this directory, creates the \"%s\"\n\
92  subdirectory depending on the username, where \n\
93  sequenceRF2D.xml file is written.\n\
94  \n\
95  -h\n\
96  Print the help.\n\n",
97  opath.c_str(), user.c_str());
98 
99  if (badparam) {
100  fprintf(stderr, "ERROR: \n");
101  fprintf(stderr, "\nBad parameter [%s]\n", badparam);
102  }
103 }
104 
114 bool getOptions(int argc, const char **argv, std::string &opath, const std::string &user)
115 {
116  const char *optarg_;
117  int c;
118  while ((c = vpParseArgv::parse(argc, argv, GETOPTARGS, &optarg_)) > 1) {
119 
120  switch (c) {
121  case 'o':
122  opath = optarg_;
123  break;
124  case 'h':
125  usage(argv[0], NULL, opath, user);
126  return false;
127  break;
128 
129  case 'c':
130  case 'd':
131  break;
132 
133  default:
134  usage(argv[0], optarg_, opath, user);
135  return false;
136  break;
137  }
138  }
139 
140  if ((c == 1) || (c == -1)) {
141  // standalone param or error
142  usage(argv[0], NULL, opath, user);
143  std::cerr << "ERROR: " << std::endl;
144  std::cerr << " Bad argument " << optarg_ << std::endl << std::endl;
145  return false;
146  }
147 
148  return true;
149 }
150 
151 int main(int argc, const char *argv[])
152 {
153  std::string opt_opath;
154  std::string username;
155 
156  std::string logFilename;
157  std::string opath;
158  std::string windowTitle;
159 
160 // Set the default output path
161 #if !defined(_WIN32) && (defined(__unix__) || defined(__unix) || (defined(__APPLE__) && defined(__MACH__))) // UNIX
162  opt_opath = "/tmp";
163 #elif defined(_WIN32)
164  opt_opath = "C:\\temp";
165 #endif
166 
167  // Get the user login name
168  vpIoTools::getUserName(username);
169 
170  // Read the command line options
171  if (getOptions(argc, argv, opt_opath, username) == false) {
172  exit(-1);
173  }
174 
175  // Get the option values
176  if (!opt_opath.empty())
177  opath = opt_opath;
178 
179  // Append to the output path string, the login name of the user
180  std::string dirname = vpIoTools::createFilePath(opath.c_str(), username);
181 
182  // Test if the output path exist. If no try to create it
183  if (vpIoTools::checkDirectory(dirname) == false) {
184  try {
185  // Create the dirname
186  vpIoTools::makeDirectory(dirname);
187  }
188  catch (...) {
189  usage(argv[0], NULL, opath, username);
190  std::cerr << std::endl << "ERROR:" << std::endl;
191  std::cerr << " Cannot create " << dirname << std::endl;
192  std::cerr << " Check your -o " << opath.c_str() << " option " << std::endl;
193  exit(-1);
194  }
195  }
196 
197  std::string xml_filename;
198 
199  for (int i = 0; i < argc; i++) {
200  if (std::string(argv[i]) == "--input")
201  xml_filename = std::string(argv[i + 1]);
202  else if (std::string(argv[i]) == "--help") {
203  std::cout << "\nUsage: " << argv[0] << " [--input <needleSequence.xml>] [--help]\n" << std::endl;
204  return 0;
205  }
206  }
207 
208  // Get the ustk-dataset package path or USTK_DATASET_PATH environment variable value
209  if (xml_filename.empty()) {
210  std::string env_ipath = us::getDataSetPath();
211  if (!env_ipath.empty())
212  xml_filename = env_ipath + "/needle/water_bath_minimal_noise_png/sequence.xml";
213  else {
214  std::cout << "You should set USTK_DATASET_PATH environment var to access to ustk dataset" << std::endl;
215  return 0;
216  }
217  }
218 
220  //
221  // Initializations.
222  //
224 
226  reader.setSequenceFileName(xml_filename);
227 
228  // Read the first image
230  reader.acquire(I);
231 
232  int n0 = 150;
233 
234 #if defined VISP_HAVE_X11
235  vpDisplay *display = new vpDisplayX(I);
236 #elif defined VISP_HAVE_GTK
237  vpDisplay *display = new vpDisplayGTK(I);
238 #elif defined VISP_HAVE_GDI
239  vpDisplay *display = new vpDisplayGDI(I);
240 #elif defined VISP_HAVE_D3D9
241  vpDisplay *display = new vpDisplayD3D(I);
242 #elif defined VISP_HAVE_OPENCV
243  vpDisplay *display = new vpDisplayOpenCV(I);
244 #else
245 #error "No display available."
246 #endif
247 
248  vpDisplay::display(I);
249  vpDisplay::flush(I);
250 
251  // Initialize the needle model
252  usPolynomialCurve2D needle(2);
253  vpMatrix controlPoints(2, 2);
254  vpImagePoint P;
255 
256  std::cout << "Click on the entry point." << std::endl;
257  vpDisplay::getClick(I, P);
258 
259  controlPoints[0][0] = P.get_i();
260  controlPoints[1][0] = P.get_j();
261 
262  std::cout << "Click on the tip." << std::endl;
263  vpDisplay::getClick(I, P);
264 
265  controlPoints[0][1] = P.get_i();
266  controlPoints[1][1] = P.get_j();
267 
268  needle.setControlPoints(controlPoints.t());
269  std::cout << "Needle model initialized." << std::endl;
270 
271  // Initialization of the needle detector
272  usNeedleTrackerSIR2D needleDetector;
273 
274  unsigned int nControlPoints = 2;
275  unsigned int nParticles = 200;
276 
277  needleDetector.setSigma(1.0);
278  needleDetector.setSigma1(10.0);
279  needleDetector.setSigma2(1.0);
280  needleDetector.init(I, nControlPoints, nParticles, needle);
281  std::cout << "Needle detector initialized." << std::endl;
282 
283  // Output
284  logFilename = dirname + std::string("/needle.dat");
285  std::ofstream ofile(logFilename.c_str());
286  std::cout << "Results will be saved in " << logFilename.c_str() << std::endl;
287 
288  unsigned int nPoints = needleDetector.getNeedle()->getOrder();
289  controlPoints = needleDetector.getNeedle()->getControlPoints();
290  ofile << nPoints;
291  for (unsigned int i = 0; i < nPoints; ++i)
292  ofile << " " << controlPoints[0][i] << " " << controlPoints[1][i];
293  ofile << std::endl;
294 
295  vpColVector tipPose, entryPose;
296 
298  //
299  // Start needle detection.
300  //
302 
303  // printf(logFilename, "%05d.%s", n0++, extension.c_str());
304 
305  unsigned int it = 1;
306  vpMatrix tipStd(2, 2);
307  vpColVector tipMean;
308  vpColVector evalue, evector;
309  vpMatrix rendering;
310 
311  while (!reader.end()) {
312  reader.acquire(I);
313  needleDetector.run(I, 0.0);
314 
315  tipMean = needleDetector.getNeedle()->getPoint(1.0);
316  entryPose = needleDetector.getNeedle()->getPoint(0.0);
317  cout << "Tip position: (" << tipMean[0] << "," << tipMean[1] << ")" << endl;
318  cout << "Needle length: " << needleDetector.getNeedle()->getLength() << endl;
319  cout << "Number of control points: " << needleDetector.getNeedle()->getOrder() << endl;
320 
321  // Output
322  nPoints = needleDetector.getNeedle()->getOrder();
323  controlPoints = needleDetector.getNeedle()->getControlPoints();
324  ofile << nPoints;
325  for (unsigned int i = 0; i < nPoints; ++i)
326  ofile << " " << controlPoints[0][i] << " " << controlPoints[1][i];
327  ofile << std::endl;
328 
329  // Display
330  int size = static_cast<int>(ceil(log10(n0 + 1))) + 1
331  char *noChar = new char[size];
332  snprintf(noChar, size, "%d", n0);
333  windowTitle = std::string("Frame ") + std::string(noChar);
334  delete[] noChar;
335  vpDisplay::setTitle(I, windowTitle);
336  vpDisplay::display(I);
337 
338  rendering = needleDetector.getNeedle()->getRenderingPoints();
339  unsigned int n = rendering.getCols();
340 
341  for (unsigned int j = 0; j < n - 1; ++j)
342  vpDisplay::displayLine(I, (int)rendering[0][j], (int)rendering[1][j], (int)rendering[0][j + 1],
343  (int)rendering[1][j + 1], vpColor::red, 2);
344 
345  tipStd = 0.0;
346 
347  for (unsigned int i = 0; i < nParticles; ++i) {
348  tipPose = needleDetector.getParticle(i)->getPoint(1.0);
349  tipStd += needleDetector.getWeight(i) * (tipPose - tipMean) * (tipPose - tipMean).t();
350 
351  if ((it % 10) == 0)
352  vpDisplay::displayCross(I, (int)tipPose[0], (int)tipPose[1], 3, vpColor::blue);
353  }
354 
355  vpDisplay::flush(I);
356 
357  // sprintf(logFilename, "%05d.%s", n0++, extension.c_str());
358 
359  // vpIoTools::setBaseName(logFilenames);
360  ++it;
361  }
362 
363 // delete and close everything
364 #if defined VISP_HAVE_DISPLAY
365  delete display;
366 #endif
367  ofile.close();
368  return 0;
369 }
370 
371 #else
372 int main() { std::cout << "You should install xml2 to use this example" << std::endl; }
373 #endif
Needle detection in 2D images based on particle filtering.
double getWeight(unsigned int i)
usPolynomialCurve2D * getNeedle()
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)
double getLength(int nbCountSeg=50) const
vpMatrix getControlPoints() const
vpColVector getPoint(double parameter) const
vpMatrix getRenderingPoints() const
unsigned int getOrder() const
Reading of sequences of ultrasound images.
void acquire(ImageType &image)
void setSequenceFileName(const std::string &sequenceFileName)
VISP_EXPORT void display(const usOrientedPlane3D &plane, const vpImage< ImageDataType > &I, const vpHomogeneousMatrix &imageMworld, double Xscale=3000, double Yscale=3000, const vpColor &color=vpColor::green)
Display usOrientedPlane3D.
VISP_EXPORT std::string getDataSetPath()
Definition: us.cpp:54