Visual Servoing Platform  version 3.6.1 under development (2024-11-15)
vpTutoCommonData.h
1 /*
2  * ViSP, open source Visual Servoing Platform software.
3  * Copyright (C) 2005 - 2024 by Inria. All rights reserved.
4  *
5  * This software is free software; you can redistribute it and/or modify
6  * it under the terms of the GNU General Public License as published by
7  * the Free Software Foundation; either version 2 of the License, or
8  * (at your option) any later version.
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 ViSP 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  * See https://visp.inria.fr for more information.
17  *
18  * This software was developed at:
19  * Inria Rennes - Bretagne Atlantique
20  * Campus Universitaire de Beaulieu
21  * 35042 Rennes Cedex
22  * France
23  *
24  * If you have questions regarding the use of this file, please contact
25  * Inria at visp@inria.fr
26  *
27  * This file is provided AS IS with NO WARRANTY OF ANY KIND, INCLUDING THE
28  * WARRANTY OF DESIGN, MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE.
29  */
30 #ifndef VP_COMMMON_DATA_H
31 #define VP_COMMMON_DATA_H
32 #include <iostream>
33 #include <string>
34 
35 #include <visp3/core/vpConfig.h>
36 #include <visp3/core/vpImage.h>
37 #include <visp3/core/vpImageFilter.h>
38 #include <visp3/core/vpIoTools.h>
39 #include <visp3/gui/vpDisplayFactory.h>
40 #include <visp3/io/vpVideoReader.h>
41 
42 #if (VISP_CXX_STANDARD >= VISP_CXX_STANDARD_11)
43 #ifndef DOXYGEN_SHOULD_SKIP_THIS
44 namespace tutorial
45 {
46 inline void log(std::ostream &os, const std::string &filename, const std::string &funName, const std::string &arrayName, const VISP_NAMESPACE_ADDRESSING vpArray2D<double> &array, const unsigned int &level = 0)
47 {
48  os << "[" << filename << "::" << funName << "] ";
49  for (unsigned int i = 0; i < level; ++i) {
50  os << "\t";
51  }
52  os << arrayName << ":=" << std::endl;
53  for (unsigned int r = 0; r < array.getRows(); ++r) {
54  for (unsigned int i = 0; i < level; ++i) {
55  os << "\t";
56  }
57  os << "[";
58  for (unsigned int c = 0; c < array.getCols() - 1; ++c) {
59  os << std::setprecision(3) << std::scientific << array[r][c] << "\t; ";
60  }
61  os << array[r][array.getCols() - 1] << "]\n";
62  }
63  os << std::flush;
64 }
65 
66 typedef struct vpTutoCommonData
67 {
68  static const int SOFTWARE_CONTINUE = 4221;
69  const VISP_NAMESPACE_ADDRESSING vpColor m_colorLegend = VISP_NAMESPACE_ADDRESSING vpColor::red;
70  const VISP_NAMESPACE_ADDRESSING vpImagePoint m_ipLegend = VISP_NAMESPACE_ADDRESSING vpImagePoint(20, 20);
71  const VISP_NAMESPACE_ADDRESSING vpImagePoint m_legendOffset = VISP_NAMESPACE_ADDRESSING vpImagePoint(20, 0);
72  std::string m_seqFilename;
73  VISP_NAMESPACE_ADDRESSING vpVideoReader m_grabber;
74  std::string m_hsvFilename;
75  VISP_NAMESPACE_ADDRESSING vpColVector m_hsv_values;
76  bool m_stepbystep;
77  double m_ratioSaltPepperNoise;
78  unsigned int m_degree;
79 
81  VISP_NAMESPACE_ADDRESSING vpImage<VISP_NAMESPACE_ADDRESSING vpRGBa> m_I_orig;
82  VISP_NAMESPACE_ADDRESSING vpImage<VISP_NAMESPACE_ADDRESSING vpRGBa> m_I_segmented;
83  VISP_NAMESPACE_ADDRESSING vpImage<unsigned char> m_mask;
84  VISP_NAMESPACE_ADDRESSING vpImage<unsigned char> m_Iskeleton;
85  VISP_NAMESPACE_ADDRESSING vpImage<unsigned char> m_IskeletonNoisy;
86 #if defined(VISP_HAVE_DISPLAY)
87  std::shared_ptr<VISP_NAMESPACE_ADDRESSING vpDisplay> m_displayOrig;
88  std::shared_ptr<VISP_NAMESPACE_ADDRESSING vpDisplay> m_displaySegmented;
89  std::shared_ptr<VISP_NAMESPACE_ADDRESSING vpDisplay> m_displayNoisy;
90 #endif
91 
93  double m_pfMaxDistanceForLikelihood;
94  unsigned int m_pfN;
95  std::vector<double> m_pfRatiosAmpliMax;
96  long m_pfSeed;
97  int m_pfNbThreads;
99  vpTutoCommonData()
100  : m_seqFilename(VISP_NAMESPACE_ADDRESSING vpIoTools::createFilePath("data", "color_image_%04d.png"))
101  , m_hsvFilename(VISP_NAMESPACE_ADDRESSING vpIoTools::createFilePath("calib", "hsv-thresholds.yml"))
102  , m_stepbystep(true)
103  , m_ratioSaltPepperNoise(0.15)
104  , m_degree(2)
105  , m_pfMaxDistanceForLikelihood(40)
106  , m_pfN(300)
107  , m_pfRatiosAmpliMax({ 0.25, 0.25, 0.25 })
108  , m_pfSeed(4221)
109  , m_pfNbThreads(-1)
110  { }
111 
117  inline void printHelp(const char *softName)
118  {
119  std::cout << "\nSYNOPSIS " << std::endl
120  << softName
121  << " [--video <input video>] [--hsv-thresholds <filename.yml>] [--noise <ratio>]" << std::endl
122  << " [--degree <uint>]" << std::endl
123  << " [--max-distance-likelihood <double>] [-N, --nb-particles <uint>] [--seed <int>] [--nb-threads <int>] [--state-noise-ratio <ratio>]" << std::endl
124  << " [--help,-h]"
125  << std::endl;
126  std::cout << "\nOPTIONS " << std::endl
127  << " [General params]" << std::endl
128  << " --video <input video>" << std::endl
129  << " Name of the input video filename." << std::endl
130  << " If name is set to \"generate-simulated\" a simulated image is generated." << std::endl
131  << " Example: --video " << this->m_seqFilename << std::endl
132  << std::endl
133  << " --hsv-thresholds <filename.yaml>" << std::endl
134  << " Path to a yaml filename that contains H <min,max>, S <min,max>, V <min,max> threshold values." << std::endl
135  << " For an example, have a look to the file \"" << this->m_hsvFilename << "\"" << std::endl
136  << std::endl
137  << " --noise <ratio, [0; 1.[ >" << std::endl
138  << " Ratio of noisy points added to the image resulting from the skeletonization of the segmented image, to simulate sensor noise." << std::endl
139  << " Default = " << this->m_ratioSaltPepperNoise << std::endl
140  << std::endl
141  << " --degree <uint>" << std::endl
142  << " Choose the degree of the polynomials to use." << std::endl
143  << " Default = " << this->m_degree << std::endl
144  << std::endl
145  << std::endl
146  << " [PF params]" << std::endl
147  << " --max-distance-likelihood" << std::endl
148  << " Maximum mean square distance between a particle with the measurements." << std::endl
149  << " Above this value, the likelihood of the particle is 0." << std::endl
150  << " NOTE: M-estimation is used to make the likelihood function robust against outliers." << std::endl
151  << " Default: " << m_pfMaxDistanceForLikelihood << std::endl
152  << std::endl
153  << " -N, --nb-particles" << std::endl
154  << " Number of particles of the Particle Filter." << std::endl
155  << " Default: " << m_pfN << std::endl
156  << std::endl
157  << " --seed" << std::endl
158  << " Seed to initialize the Particle Filter." << std::endl
159  << " Use a negative value makes to use the current timestamp instead." << std::endl
160  << " Default: " << m_pfSeed << std::endl
161  << std::endl
162  << " --nb-threads" << std::endl
163  << " Set the number of threads to use in the Particle Filter (only if OpenMP is available)." << std::endl
164  << " Use a negative value to use the maximum number of threads instead." << std::endl
165  << " Default: " << m_pfNbThreads << std::endl
166  << std::endl
167  << " --state-noise-ratio <ratio>" << std::endl
168  << " Ratio of the initial guess of the curve coefficients to use as maximal amplitude of the noise added to the particles." << std::endl
169  << " Default: " << m_pfRatiosAmpliMax[0] << std::endl
170  << " --help, -h" << std::endl
171  << " Display this helper message." << std::endl
172  << std::endl;
173  }
174 
183  inline int init(const int &argc, const char *argv[])
184  {
185  // Parse the input arguments
186  int i = 1;
187  while (i < argc) {
188  std::string argname(argv[i]);
189  if ((argname == std::string("--video")) && ((i + 1) < argc)) {
190  ++i;
191  m_seqFilename = std::string(argv[i]);
192  }
193  else if ((argname == std::string("--hsv-thresholds")) && ((i + 1) < argc)) {
194  ++i;
195  m_hsvFilename = std::string(argv[i]);
196  }
197  else if ((argname == "--noise") && ((i + 1) < argc)) {
198  ++i;
199  m_ratioSaltPepperNoise = std::atof(argv[i]);
200  }
201  else if ((argname == std::string("--degree")) && ((i + 1) < argc)) {
202  ++i;
203  m_degree = std::atoi(argv[i]);
204  }
205  else if ((argname == "--max-distance-likelihood") && ((i+1) < argc)) {
206  ++i;
207  m_pfMaxDistanceForLikelihood = std::atof(argv[i]);
208  }
209  else if (((argname == "-N") || (argname == "--nb-particles")) && ((i+1) < argc)) {
210  ++i;
211  m_pfN = std::atoi(argv[i]);
212  }
213  else if ((argname == "--seed") && ((i+1) < argc)) {
214  ++i;
215  m_pfSeed = std::atoi(argv[i]);
216  }
217  else if ((argname == "--nb-threads") && ((i+1) < argc)) {
218  ++i;
219  m_pfNbThreads = std::atoi(argv[i]);
220  }
221  else if ((argname == "--state-noise-ratio") && ((i+1) < argc)) {
222  ++i;
223  m_pfRatiosAmpliMax[0] = std::atof(argv[i]);
224  }
225  else if ((argname == std::string("-h")) || (argname == std::string("--help"))) {
226  vpTutoCommonData helpPrinter;
227  helpPrinter.printHelp(argv[0]);
228  return EXIT_SUCCESS;
229  }
230  else {
231  std::cerr << "Unknown argument \"" << argname << "\"" << std::endl;
232  return EXIT_FAILURE;
233  }
234  ++i;
235  }
236 
237  // Ensure that the maximal amplitude vector is of correct size and values
238  m_pfRatiosAmpliMax.resize(m_degree, m_pfRatiosAmpliMax[0]);
239 
240  // Load the HSV thresholds
241  if (VISP_NAMESPACE_ADDRESSING vpColVector::loadYAML(m_hsvFilename, m_hsv_values)) {
242  std::cout << "Load HSV threshold values from " << m_hsvFilename << std::endl;
243  std::cout << "HSV low/high values: " << m_hsv_values.t() << std::endl;
244  }
245  else {
246  std::cout << "ERROR: unable to load HSV thresholds values from " << m_hsvFilename << std::endl;
247  return EXIT_FAILURE;
248  }
249 
250  // Open the sequence of images
251  try {
252  m_grabber.setFileName(m_seqFilename);
253  m_grabber.open(m_I_orig);
254  }
255  catch (const VISP_NAMESPACE_ADDRESSING vpException &e) {
256  std::cout << e.getStringMessage() << std::endl;
257  return EXIT_FAILURE;
258  }
259 
260  m_I_segmented.resize(m_I_orig.getHeight(), m_I_orig.getWidth()); // Resize the segmented image to match the original image
261  m_mask.resize(m_I_orig.getHeight(), m_I_orig.getWidth()); // Resize the binary mask that indicates which pixels are in the allowed HSV range.
262  m_Iskeleton.resize(m_I_orig.getHeight(), m_I_orig.getWidth()); // Resize the edge-map.
263  m_IskeletonNoisy.resize(m_I_orig.getHeight(), m_I_orig.getWidth()); // Resize the edge-map.
264 
265  // Init the displays
266 #if defined(VISP_HAVE_DISPLAY)
267  const int horOffset = 20, vertOffset = 25;
268  std::string skeletonTitle("Skeletonized image (");
269  skeletonTitle += (m_ratioSaltPepperNoise == 0 ? "without" : std::to_string(static_cast<unsigned int>(m_ratioSaltPepperNoise * 100.)) + "%");
270  skeletonTitle += " noise)";
271  m_displayOrig = VISP_NAMESPACE_ADDRESSING vpDisplayFactory::createDisplay(m_I_orig, horOffset, vertOffset, "Original image");
272  m_displaySegmented = VISP_NAMESPACE_ADDRESSING vpDisplayFactory::createDisplay(m_I_segmented, 2 * horOffset + m_I_orig.getWidth(), vertOffset, "Segmented image");
273  m_displayNoisy = VISP_NAMESPACE_ADDRESSING vpDisplayFactory::createDisplay(m_IskeletonNoisy, 2 * horOffset + m_I_orig.getWidth(), 2 * vertOffset + m_I_orig.getHeight(), skeletonTitle);
274 #endif
275  return SOFTWARE_CONTINUE;
276  }
277 
278 #ifdef VISP_HAVE_DISPLAY
279  template<typename T>
280  void displayLegend(const VISP_NAMESPACE_ADDRESSING vpImage<T> &I)
281  {
282  VISP_NAMESPACE_ADDRESSING vpImagePoint ip(20, 20);
283  VISP_NAMESPACE_ADDRESSING vpImagePoint offset(20, 0);
284  if (m_stepbystep) {
285  VISP_NAMESPACE_ADDRESSING vpDisplay::displayText(I, ip, std::string("Left click to switch to next image"), VISP_NAMESPACE_ADDRESSING vpColor::red);
286  }
287  VISP_NAMESPACE_ADDRESSING vpDisplay::displayText(I, ip + offset, std::string("Middle click to switch to ") + (m_stepbystep ? std::string("video mode") : std::string("step-by-step mode")), VISP_NAMESPACE_ADDRESSING vpColor::red);
288  VISP_NAMESPACE_ADDRESSING vpDisplay::displayText(I, ip + offset + offset, std::string("Right click to quit"), VISP_NAMESPACE_ADDRESSING vpColor::red);
289  }
290 
291  template<typename T>
292  bool manageClicks(const VISP_NAMESPACE_ADDRESSING vpImage<T> &I, bool &stepbystep)
293  {
294  VISP_NAMESPACE_ADDRESSING vpImagePoint ip;
295  VISP_NAMESPACE_ADDRESSING vpMouseButton::vpMouseButtonType button;
296  VISP_NAMESPACE_ADDRESSING vpDisplay::getClick(I, ip, button, stepbystep);
297  if (button == VISP_NAMESPACE_ADDRESSING vpMouseButton::vpMouseButtonType::button3) {
298  return false;
299  }
300  if (button == VISP_NAMESPACE_ADDRESSING vpMouseButton::vpMouseButtonType::button2) {
301  stepbystep = stepbystep ^ true;
302  }
303  return true;
304  }
305 #endif
306 }vpTutoCommonData;
307 }
308 #endif
309 #endif
310 #endif
static bool loadYAML(const std::string &filename, vpArray2D< double > &A, char *header=nullptr)
Definition: vpArray2D.h:783
Implementation of column vector and the associated operations.
Definition: vpColVector.h:191
Class to define RGB colors available for display functionalities.
Definition: vpColor.h:157
static const vpColor red
Definition: vpColor.h:217
static bool getClick(const vpImage< unsigned char > &I, bool blocking=true)
static void displayText(const vpImage< unsigned char > &I, const vpImagePoint &ip, const std::string &s, const vpColor &color)
error that can be emitted by ViSP classes.
Definition: vpException.h:60
Class that defines a 2D point in an image. This class is useful for image processing and stores only ...
Definition: vpImagePoint.h:82
Definition of the vpImage class member functions.
Definition: vpImage.h:131
File and directories basic tools.
Definition: vpIoTools.h:505
Class that enables to manipulate easily a video file or a sequence of images. As it inherits from the...
std::shared_ptr< vpDisplay > createDisplay()
Return a smart pointer vpDisplay specialization if a GUI library is available or nullptr otherwise.