Visual Servoing Platform version 3.7.0
Loading...
Searching...
No Matches
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/core/vpMath.h>
40#include <visp3/gui/vpDisplayFactory.h>
41#include <visp3/io/vpVideoReader.h>
42
43#if (VISP_CXX_STANDARD >= VISP_CXX_STANDARD_11)
44#ifndef DOXYGEN_SHOULD_SKIP_THIS
45namespace tutorial
46{
47inline 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)
48{
49 os << "[" << filename << "::" << funName << "] ";
50 for (unsigned int i = 0; i < level; ++i) {
51 os << "\t";
52 }
53 os << arrayName << ":=" << std::endl;
54 for (unsigned int r = 0; r < array.getRows(); ++r) {
55 for (unsigned int i = 0; i < level; ++i) {
56 os << "\t";
57 }
58 os << "[";
59 for (unsigned int c = 0; c < array.getCols() - 1; ++c) {
60 os << std::setprecision(3) << std::scientific << array[r][c] << "\t; ";
61 }
62 os << array[r][array.getCols() - 1] << "]\n";
63 }
64 os << std::flush;
65}
66
67typedef struct vpTutoCommonData
68{
69 static const int SOFTWARE_CONTINUE = 4221;
70 const VISP_NAMESPACE_ADDRESSING vpColor m_colorLegend = VISP_NAMESPACE_ADDRESSING vpColor::red;
71 const VISP_NAMESPACE_ADDRESSING vpImagePoint m_ipLegend = VISP_NAMESPACE_ADDRESSING vpImagePoint(20, 20);
72 const VISP_NAMESPACE_ADDRESSING vpImagePoint m_legendOffset = VISP_NAMESPACE_ADDRESSING vpImagePoint(20, 0);
73 std::string m_seqFilename;
74 VISP_NAMESPACE_ADDRESSING vpVideoReader m_grabber;
75 std::string m_hsvFilename;
76 VISP_NAMESPACE_ADDRESSING vpColVector m_hsv_values;
77 bool m_stepbystep;
78 double m_ratioSaltPepperNoise;
79 unsigned int m_degree;
80
82 VISP_NAMESPACE_ADDRESSING vpImage<VISP_NAMESPACE_ADDRESSING vpRGBa> m_I_orig;
83 VISP_NAMESPACE_ADDRESSING vpImage<VISP_NAMESPACE_ADDRESSING vpRGBa> m_I_segmented;
84 VISP_NAMESPACE_ADDRESSING vpImage<unsigned char> m_mask;
85 VISP_NAMESPACE_ADDRESSING vpImage<unsigned char> m_Iskeleton;
86 VISP_NAMESPACE_ADDRESSING vpImage<unsigned char> m_IskeletonNoisy;
87#if defined(VISP_HAVE_DISPLAY)
88 std::shared_ptr<VISP_NAMESPACE_ADDRESSING vpDisplay> m_displayOrig;
89 std::shared_ptr<VISP_NAMESPACE_ADDRESSING vpDisplay> m_displaySegmented;
90 std::shared_ptr<VISP_NAMESPACE_ADDRESSING vpDisplay> m_displayNoisy;
91#endif
92
94 double m_pfMaxDistanceForLikelihood;
95 unsigned int m_pfN;
96 std::vector<double> m_pfRatiosAmpliMax;
97 long m_pfSeed;
98 int m_pfNbThreads;
99
100 vpTutoCommonData()
101 : m_seqFilename(VISP_NAMESPACE_ADDRESSING vpIoTools::createFilePath("data", "color_image_%04d.png"))
102 , m_hsvFilename(VISP_NAMESPACE_ADDRESSING vpIoTools::createFilePath("calib", "hsv-thresholds.yml"))
103 , m_stepbystep(true)
104 , m_ratioSaltPepperNoise(0.15)
105 , m_degree(2)
106 , m_pfMaxDistanceForLikelihood(40)
107 , m_pfN(300)
108 , m_pfRatiosAmpliMax({ 0.25, 0.25, 0.25 })
109 , m_pfSeed(4221)
110 , m_pfNbThreads(-1)
111 { }
112
118 inline void printHelp(const char *softName)
119 {
120 std::cout << "\nSYNOPSIS " << std::endl
121 << softName
122 << " [--video <input video>] [--hsv-thresholds <filename.yml>] [--noise <ratio>]" << std::endl
123 << " [--degree <uint>]" << std::endl
124 << " [--max-distance-likelihood <double>] [-N, --nb-particles <uint>] [--seed <int>] [--nb-threads <int>] [--state-noise-ratio <ratio>]" << std::endl
125 << " [--help,-h]"
126 << std::endl;
127 std::cout << "\nOPTIONS " << std::endl
128 << " [General params]" << std::endl
129 << " --video <input video>" << std::endl
130 << " Name of the input video filename." << std::endl
131 << " If name is set to \"generate-simulated\" a simulated image is generated." << std::endl
132 << " Example: --video " << this->m_seqFilename << std::endl
133 << std::endl
134 << " --hsv-thresholds <filename.yaml>" << std::endl
135 << " Path to a yaml filename that contains H <min,max>, S <min,max>, V <min,max> threshold values." << std::endl
136 << " For an example, have a look to the file \"" << this->m_hsvFilename << "\"" << std::endl
137 << std::endl
138 << " --noise <ratio, [0; 1.[ >" << std::endl
139 << " Ratio of noisy points added to the image resulting from the skeletonization of the segmented image, to simulate sensor noise." << std::endl
140 << " Default = " << this->m_ratioSaltPepperNoise << std::endl
141 << std::endl
142 << " --degree <uint>" << std::endl
143 << " Choose the degree of the polynomials to use." << std::endl
144 << " Default = " << this->m_degree << std::endl
145 << std::endl
146 << std::endl
147 << " [PF params]" << std::endl
148 << " --max-distance-likelihood" << std::endl
149 << " Maximum mean square distance between a particle with the measurements." << std::endl
150 << " Above this value, the likelihood of the particle is 0." << std::endl
151 << " NOTE: M-estimation is used to make the likelihood function robust against outliers." << std::endl
152 << " Default: " << m_pfMaxDistanceForLikelihood << std::endl
153 << std::endl
154 << " -N, --nb-particles" << std::endl
155 << " Number of particles of the Particle Filter." << std::endl
156 << " Default: " << m_pfN << std::endl
157 << std::endl
158 << " --seed" << std::endl
159 << " Seed to initialize the Particle Filter." << std::endl
160 << " Use a negative value makes to use the current timestamp instead." << std::endl
161 << " Default: " << m_pfSeed << std::endl
162 << std::endl
163 << " --nb-threads" << std::endl
164 << " Set the number of threads to use in the Particle Filter (only if OpenMP is available)." << std::endl
165 << " Use a negative value to use the maximum number of threads instead." << std::endl
166 << " Default: " << m_pfNbThreads << std::endl
167 << std::endl
168 << " --state-noise-ratio <ratio>" << std::endl
169 << " Ratio of the initial guess of the curve coefficients to use as maximal amplitude of the noise added to the particles." << std::endl
170 << " Default: " << m_pfRatiosAmpliMax[0] << std::endl
171 << " --help, -h" << std::endl
172 << " Display this helper message." << std::endl
173 << std::endl;
174 }
175
184 inline int init(const int &argc, const char *argv[])
185 {
186 // Parse the input arguments
187 int i = 1;
188 while (i < argc) {
189 std::string argname(argv[i]);
190 if ((argname == std::string("--video")) && ((i + 1) < argc)) {
191 ++i;
192 m_seqFilename = std::string(argv[i]);
193 }
194 else if ((argname == std::string("--hsv-thresholds")) && ((i + 1) < argc)) {
195 ++i;
196 m_hsvFilename = std::string(argv[i]);
197 }
198 else if ((argname == "--noise") && ((i + 1) < argc)) {
199 ++i;
200 m_ratioSaltPepperNoise = std::atof(argv[i]);
201 }
202 else if ((argname == std::string("--degree")) && ((i + 1) < argc)) {
203 ++i;
204 m_degree = std::atoi(argv[i]);
205 }
206 else if ((argname == "--max-distance-likelihood") && ((i+1) < argc)) {
207 ++i;
208 m_pfMaxDistanceForLikelihood = std::atof(argv[i]);
209 }
210 else if (((argname == "-N") || (argname == "--nb-particles")) && ((i+1) < argc)) {
211 ++i;
212 m_pfN = std::atoi(argv[i]);
213 }
214 else if ((argname == "--seed") && ((i+1) < argc)) {
215 ++i;
216 m_pfSeed = std::atoi(argv[i]);
217 }
218 else if ((argname == "--nb-threads") && ((i+1) < argc)) {
219 ++i;
220 m_pfNbThreads = std::atoi(argv[i]);
221 }
222 else if ((argname == "--state-noise-ratio") && ((i+1) < argc)) {
223 ++i;
224 m_pfRatiosAmpliMax[0] = std::atof(argv[i]);
225 }
226 else if ((argname == std::string("-h")) || (argname == std::string("--help"))) {
227 vpTutoCommonData helpPrinter;
228 helpPrinter.printHelp(argv[0]);
229 return EXIT_SUCCESS;
230 }
231 else {
232 std::cerr << "Unknown argument \"" << argname << "\"" << std::endl;
233 return EXIT_FAILURE;
234 }
235 ++i;
236 }
237
238 // Ensure that the maximal amplitude vector is of correct size and values
239 m_pfRatiosAmpliMax.resize(m_degree, m_pfRatiosAmpliMax[0]);
240
241 // Load the HSV thresholds
242 if (VISP_NAMESPACE_ADDRESSING vpColVector::loadYAML(m_hsvFilename, m_hsv_values)) {
243 std::cout << "Load HSV threshold values from " << m_hsvFilename << std::endl;
244 std::cout << "HSV low/high values: " << m_hsv_values.t() << std::endl;
245 }
246 else {
247 std::cout << "ERROR: unable to load HSV thresholds values from " << m_hsvFilename << std::endl;
248 return EXIT_FAILURE;
249 }
250
251 // Open the sequence of images
252 try {
253 m_grabber.setFileName(m_seqFilename);
254 m_grabber.open(m_I_orig);
255 }
256 catch (const VISP_NAMESPACE_ADDRESSING vpException &e) {
257 std::cout << e.getStringMessage() << std::endl;
258 return EXIT_FAILURE;
259 }
260
261 m_I_segmented.resize(m_I_orig.getHeight(), m_I_orig.getWidth()); // Resize the segmented image to match the original image
262 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.
263 m_Iskeleton.resize(m_I_orig.getHeight(), m_I_orig.getWidth()); // Resize the edge-map.
264 m_IskeletonNoisy.resize(m_I_orig.getHeight(), m_I_orig.getWidth()); // Resize the edge-map.
265
266 // Init the displays
267#if defined(VISP_HAVE_DISPLAY)
268 const int horOffset = 20, vertOffset = 25;
269 std::string skeletonTitle("Skeletonized image (");
270 skeletonTitle += (VISP_NAMESPACE_ADDRESSING vpMath::equal(m_ratioSaltPepperNoise, 0.) ? "without" : std::to_string(static_cast<unsigned int>(m_ratioSaltPepperNoise * 100.)) + "%");
271 skeletonTitle += " noise)";
272 m_displayOrig = VISP_NAMESPACE_ADDRESSING vpDisplayFactory::createDisplay(m_I_orig, horOffset, vertOffset, "Original image");
273 m_displaySegmented = VISP_NAMESPACE_ADDRESSING vpDisplayFactory::createDisplay(m_I_segmented, 2 * horOffset + m_I_orig.getWidth(), vertOffset, "Segmented image");
274 m_displayNoisy = VISP_NAMESPACE_ADDRESSING vpDisplayFactory::createDisplay(m_IskeletonNoisy, 2 * horOffset + m_I_orig.getWidth(), 2 * vertOffset + m_I_orig.getHeight(), skeletonTitle);
275#endif
276 return SOFTWARE_CONTINUE;
277 }
278
279#ifdef VISP_HAVE_DISPLAY
280 template<typename T>
281 void displayLegend(const VISP_NAMESPACE_ADDRESSING vpImage<T> &I)
282 {
283 VISP_NAMESPACE_ADDRESSING vpImagePoint ip(20, 20);
284 VISP_NAMESPACE_ADDRESSING vpImagePoint offset(20, 0);
285 if (m_stepbystep) {
286 VISP_NAMESPACE_ADDRESSING vpDisplay::displayText(I, ip, std::string("Left click to switch to next image"), VISP_NAMESPACE_ADDRESSING vpColor::red);
287 }
288 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);
289 VISP_NAMESPACE_ADDRESSING vpDisplay::displayText(I, ip + offset + offset, std::string("Right click to quit"), VISP_NAMESPACE_ADDRESSING vpColor::red);
290 }
291
292 template<typename T>
293 bool manageClicks(const VISP_NAMESPACE_ADDRESSING vpImage<T> &I, bool &stepbystep)
294 {
295 VISP_NAMESPACE_ADDRESSING vpImagePoint ip;
296 VISP_NAMESPACE_ADDRESSING vpMouseButton::vpMouseButtonType button;
297 VISP_NAMESPACE_ADDRESSING vpDisplay::getClick(I, ip, button, stepbystep);
298 if (button == VISP_NAMESPACE_ADDRESSING vpMouseButton::vpMouseButtonType::button3) {
299 return false;
300 }
301 if (button == VISP_NAMESPACE_ADDRESSING vpMouseButton::vpMouseButtonType::button2) {
302 stepbystep = stepbystep ^ true;
303 }
304 return true;
305 }
306#endif
307} vpTutoCommonData;
308}
309#endif
310#endif
311#endif
static bool loadYAML(const std::string &filename, vpArray2D< double > &A, char *header=nullptr)
Definition vpArray2D.h:874
static const vpColor red
Definition vpColor.h:198
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)
static bool equal(double x, double y, double threshold=0.001)
Definition vpMath.h:470
std::shared_ptr< vpDisplay > createDisplay()
Return a smart pointer vpDisplay specialization if a GUI library is available or nullptr otherwise.