Visual Servoing Platform version 3.7.0
Loading...
Searching...
No Matches
render-based-tutorial-utils.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_RB_TRACKER_TUTORIAL_HELPER_H
31#define VP_RB_TRACKER_TUTORIAL_HELPER_H
32
33#include <cstdlib>
34#include <iostream>
35#include <sstream>
36#include <sys/stat.h>
37
38#include <fstream>
39#include <math.h>
40#include <string.h>
41
42#include <visp3/core/vpImage.h>
43#include <visp3/core/vpImageFilter.h>
44#include <visp3/core/vpImageConvert.h>
45#include <visp3/core/vpImageTools.h>
46#include <visp3/core/vpIoTools.h>
47#include <visp3/core/vpDisplay.h>
48#include <visp3/core/vpTime.h>
49#include <visp3/gui/vpDisplayFactory.h>
50
51#include <visp3/io/vpImageIo.h>
52#include <visp3/io/vpJsonArgumentParser.h>
53#include <visp3/io/vpVideoWriter.h>
54
55#include <visp3/gui/vpPlot.h>
56
57#include <visp3/rbt/vpRBTracker.h>
58#include <visp3/rbt/vpObjectMask.h>
59#include <visp3/rbt/vpRBDriftDetector.h>
60
61#include "pStatClient.h"
62
63#ifndef DOXYGEN_SHOULD_SKIP_THIS
64namespace vpRBTrackerTutorial
65{
66
67#ifdef ENABLE_VISP_NAMESPACE
68using namespace VISP_NAMESPACE_NAME;
69#endif
70
71
76struct BaseArguments
77{
78 BaseArguments() : trackerConfiguration(""), maxDepthDisplay(1.f), verbose(false), display(true), debugDisplay(false), enableRenderProfiling(false) { }
79
80#if defined(VISP_HAVE_NLOHMANN_JSON)
81 void registerArguments(vpJsonArgumentParser &parser)
82 {
83 parser
84 .addArgument("--tracker", trackerConfiguration, false, "Path to the JSON file containing the tracker")
85 .addArgument("--object", object, false, "Name of the object to track. Used to potentially fetch the init file")
86 .addArgument("--init-file", initFile, false, "Path to the JSON file containing the 2D/3D correspondences for initialization by click")
87 .addArgument("--pose", inlineInit, false, "Initial pose of the object in the camera frame.")
88 .addArgument("--max-depth-display", maxDepthDisplay, false, "Maximum depth value, used to scale the depth display")
89 .addFlag("--verbose", verbose, "Log additional information in console")
90 .addFlag("--no-display", display, "Disable display windows")
91 .addFlag("--debug-display", debugDisplay, "Enable additional displays from the renderer")
92 .addFlag("--profile", enableRenderProfiling, "Enable the use of Pstats to profile rendering times");
93 }
94#endif
95
96 void postProcessArguments()
97 {
98 if (trackerConfiguration.empty()) {
99 throw vpException(vpException::badValue, "No tracker configuration was specified");
100 }
101 if (object.empty()) {
102
103 object = vpIoTools::getNameWE(trackerConfiguration);
104 }
105 else {
106 modelPath = object;
107 object = vpIoTools::getNameWE(modelPath);
108 }
109 if (initFile.empty()) {
110 initFile = vpIoTools::getParent(modelPath.empty() ? trackerConfiguration : modelPath) + vpIoTools::separator + object + ".init";
111 }
112
113 if (!display && inlineInit.empty()) {
114 throw vpException(vpException::badValue, "Cannot disable displays without specifying the initial pose");
115 }
116 if (inlineInit.size() > 0) {
117 if (inlineInit.size() != 6) {
118 throw vpException(vpException::dimensionError, "Inline pose initialization expected to have 6 values (tx, ty, tz, tux, tuy, tuz)");
119 }
120 for (unsigned int i = 0; i < 6; ++i) {
121 std::cout << "inline i = " << inlineInit[i] << std::endl;
122 }
123 cMoInit = vpHomogeneousMatrix(inlineInit[0], inlineInit[1], inlineInit[2], inlineInit[3], inlineInit[4], inlineInit[5]);
124 }
125 }
126
127 bool hasInlineInit()
128 {
129 return !inlineInit.empty();
130 }
131
132 std::string trackerConfiguration;
133 std::string object;
134 std::string modelPath;
135 std::string initFile;
136 std::vector<double> inlineInit;
137 float maxDepthDisplay;
138 vpHomogeneousMatrix cMoInit;
139 bool verbose;
140 bool display;
141 bool debugDisplay;
142 bool enableRenderProfiling;
143};
144
154class vpRBExperimentLogger
155{
156public:
157 vpRBExperimentLogger() : enabled(false), videoEnabled(false), framerate(30)
158 { }
159
160#if defined(VISP_HAVE_NLOHMANN_JSON)
161 void registerArguments(vpJsonArgumentParser &parser)
162 {
163 parser
164 .addFlag("--save", enabled, "Whether to save experiment data")
165 .addArgument("--save-path", folder, false, "Where to save the experiment log. The folder should not exist.")
166 .addFlag("--save-video", videoEnabled, "Whether to save the video")
167 .addArgument("--video-framerate", framerate, false, "Output video framerate");
168 }
169
170#endif
171
172 void startLog()
173 {
174 if (enabled) {
175 if (folder.empty()) {
176 throw vpException(vpException::badValue, "Experiment logging enabled but folder not specified");
177 }
179 if (videoEnabled) {
180#ifdef VISP_HAVE_OPENCV
181 videoWriter.setFramerate(framerate);
182 videoWriter.setCodec(cv::VideoWriter::fourcc('P', 'I', 'M', '1'));
183 videoWriter.setFileName(folder + vpIoTools::separator + "video.mp4");
184#endif
185 }
186 }
187 }
188
189 void logFrame(const vpRBTracker &
190#if defined(VISP_HAVE_NLOHMANN_JSON)
191 tracker
192#endif
193 , unsigned int iter, const vpImage<unsigned char> &I, const vpImage<vpRGBa> &IRGB,
194 const vpImage<unsigned char> &Idepth, const vpImage<unsigned char> &Imask)
195 {
196 if (!enabled) {
197 return;
198 }
199
200 if (videoEnabled) {
201 Iout.resize(IRGB.getHeight() * 2, IRGB.getWidth() * 2);
202
203 vpDisplay::getImage(I, IgrayOverlay);
204 vpDisplay::getImage(IRGB, IColOverlay);
205 vpDisplay::getImage(Idepth, IdepthOverlay);
206 vpDisplay::getImage(Imask, ImaskOverlay);
207#ifdef VISP_HAVE_OPENMP
208#pragma omp parallel for
209#endif
210 for (int i = 0; i < static_cast<int>(IRGB.getHeight()); ++i) {
211 memcpy(Iout[i], IgrayOverlay[i], IRGB.getWidth() * sizeof(vpRGBa));
212 memcpy(Iout[i] + IRGB.getWidth(), IColOverlay[i], IRGB.getWidth() * sizeof(vpRGBa));
213 memcpy(Iout[i + IRGB.getHeight()], IdepthOverlay[i], IRGB.getWidth() * sizeof(vpRGBa));
214 memcpy(Iout[i + IRGB.getHeight()] + IRGB.getWidth(), ImaskOverlay[i], IRGB.getWidth() * sizeof(vpRGBa));
215 }
216
217 if (iter == 1) {
218 videoWriter.open(Iout);
219 }
220 else {
221 videoWriter.saveFrame(Iout);
222 }
223 }
224#if defined(VISP_HAVE_NLOHMANN_JSON)
225 nlohmann::json iterLog;
226 vpHomogeneousMatrix cMo;
227 tracker.getPose(cMo);
228 iterLog["cMo"] = cMo;
229
230 log.push_back(iterLog);
231#endif
232 }
233
234 void close()
235 {
236 if (videoEnabled) {
237 videoWriter.close();
238 }
239
240#if defined(VISP_HAVE_NLOHMANN_JSON)
241 std::ofstream f(folder + vpIoTools::separator + "log.json");
242 f << log.dump(2) << std::endl;
243 f.close();
244#endif
245 }
246
247private:
248 bool enabled;
249 std::string folder;
250
251 vpImage<vpRGBa> IColOverlay;
252 vpImage<vpRGBa> IgrayOverlay;
253 vpImage<vpRGBa> IdepthOverlay;
254 vpImage<vpRGBa> ImaskOverlay;
255 vpImage<vpRGBa> Iout;
256
257 bool videoEnabled;
258 unsigned int framerate;
259 vpVideoWriter videoWriter;
260
261#if defined(VISP_HAVE_NLOHMANN_JSON)
262 nlohmann::json log;
263#endif
264};
265
270class vpRBExperimentPlotter
271{
272public:
273
274 vpRBExperimentPlotter() : enabled(false), plotPose(false), plotPose3d(false), plotDivergenceMetrics(false), plotCovariance(false) { }
275
276#if defined(VISP_HAVE_NLOHMANN_JSON)
277 void registerArguments(vpJsonArgumentParser &parser)
278 {
279 parser
280 .addFlag("--plot-pose", plotPose, "Plot the pose of the object in the camera frame")
281 .addFlag("--plot-position", plotPose3d, "Plot the position of the object in a 3d figure")
282 .addFlag("--plot-divergence", plotDivergenceMetrics, "Plot the metrics associated to the divergence threshold computation")
283 .addFlag("--plot-cov", plotCovariance, "Plot the pose covariance trace for each feature");
284 }
285#endif
286
287 void postProcessArguments(bool displayEnabled)
288 {
289 enabled = plotPose || plotDivergenceMetrics || plotPose3d || plotCovariance;
290 if (enabled && !displayEnabled) {
291 throw vpException(vpException::badValue, "Tried to plot data, but display is disabled");
292 }
293 }
294
295 void init(std::vector<std::shared_ptr<vpDisplay>> &displays)
296 {
297 if (!enabled) {
298 return;
299 }
300 int ypos = 0, xpos = 0;
301 for (std::shared_ptr<vpDisplay> &display : displays) {
302 ypos = std::min(ypos, display->getWindowYPosition());
303 xpos = std::max(xpos, display->getWindowXPosition() + static_cast<int>(display->getWidth()));
304 }
305
306 numPlots = static_cast<int>(plotPose) + static_cast<int>(plotDivergenceMetrics) + static_cast<int>(plotPose3d) + static_cast<int>(plotCovariance);
307 plotter.init(numPlots, 600, 800, xpos, ypos, "Plot");
308 unsigned int plotIndex = 0;
309 if (plotPose) {
310 plotter.initGraph(plotIndex, 6);
311 plotter.setTitle(plotIndex, "cMo");
312 std::vector<std::string> legends = {
313 "tx", "ty", "tz", "tux", "tuy", "tuz"
314 };
315 for (unsigned int i = 0; i < 6; ++i) {
316 plotter.setLegend(plotIndex, i, legends[i]);
317 }
318 plotter.setGraphThickness(plotIndex, 2);
319 ++plotIndex;
320 }
321 if (plotPose3d) {
322 plotter.initGraph(plotIndex, 1);
323 plotter.setTitle(plotIndex, "3D object position");
324 plotter.setGraphThickness(plotIndex, 2);
325 ++plotIndex;
326 }
327
328 if (plotDivergenceMetrics) {
329 plotter.initGraph(plotIndex, 1);
330 plotter.initRange(plotIndex, 0.0, 1.0, 0.0, 1.0);
331 plotter.setTitle(plotIndex, "Divergence");
332 ++plotIndex;
333 }
334 if (plotCovariance) {
335 plotter.initGraph(plotIndex, 2);
336 plotter.setLegend(plotIndex, 0, "Translation trace standard deviation (cm)");
337 plotter.setLegend(plotIndex, 1, "Rotation trace standard deviation (deg)");
338
339 plotter.setTitle(plotIndex, "Covariance");
340 ++plotIndex;
341 }
342 }
343
344 void plot(const vpRBTracker &tracker, double time)
345 {
346 if (!enabled) {
347 return;
348 }
349 unsigned int plotIndex = 0;
350 if (plotPose) {
351 vpHomogeneousMatrix cMo;
352 tracker.getPose(cMo);
353 plotter.plot(plotIndex, time, vpPoseVector(cMo));
354 ++plotIndex;
355 }
356 if (plotPose3d) {
357 vpHomogeneousMatrix cMo;
358 tracker.getPose(cMo);
359 vpTranslationVector t = cMo.getTranslationVector();
360 plotter.plot(plotIndex, 0, t[0], t[1], t[2]);
361 ++plotIndex;
362 }
363 if (plotDivergenceMetrics) {
364 const std::shared_ptr<const vpRBDriftDetector> driftDetector = tracker.getDriftDetector();
365 double metric = driftDetector ? driftDetector->getScore() : 0.0;
366 plotter.plot(plotIndex, 0, time, metric);
367 ++plotIndex;
368 }
369 if (plotCovariance) {
370 vpMatrix cov = tracker.getCovariance();
371 double traceTranslation = 0.0, traceRotation = 0.0;
372 for (unsigned int i = 0; i < 3; ++i) {
373 traceTranslation += cov[i][i];
374 traceRotation += cov[i + 3][i + 3];
375 }
376 traceTranslation = sqrt(traceTranslation) * 100;
377 traceRotation = vpMath::deg(sqrt(traceRotation));
378
379 plotter.plot(plotIndex, 0, time, traceTranslation);
380 plotter.plot(plotIndex, 1, time, traceRotation);
381
382 ++plotIndex;
383 }
384 }
385private:
386 bool enabled;
387 bool plotPose;
388 bool plotPose3d;
389 bool plotDivergenceMetrics;
390 bool plotCovariance;
391 int numPlots;
392 vpPlot plotter;
393};
394
395
405std::vector<std::shared_ptr<vpDisplay>> createDisplays(
406 vpImage<unsigned char> &Id, vpImage<vpRGBa> &Icol,
407 vpImage<unsigned char> &depthDisplay, vpImage<unsigned char> &probaDisplay)
408{
410 2, 2,
411 0, 0,
412 80, 80,
413 "Grayscale", Id,
414 "Color", Icol,
415 "Depth", depthDisplay,
416 "Proba mask", probaDisplay
417 );
418}
419
420std::vector<std::shared_ptr<vpDisplay>> createDisplays(
421 vpImage<unsigned char> &Id, vpImage<vpRGBa> &Icol, vpImage<unsigned char> &probaDisplay)
422{
424 1, 3,
425 0, 0,
426 80, 80,
427 "Grayscale", Id,
428 "Color", Icol,
429 "Proba mask", probaDisplay
430 );
431}
432
436void enableRendererProfiling()
437{
438 if (PStatClient::is_connected()) {
439 PStatClient::disconnect();
440 }
441
442 std::string host = ""; // Empty = default config var value
443 int port = -1; // -1 = default config var value
444 if (!PStatClient::connect(host, port)) {
445 std::cout << "Could not connect to PStat server." << std::endl;
446 }
447}
448
455void displayNormals(const vpImage<vpRGBf> &normalsImage, vpImage<vpRGBa> &normalDisplayImage)
456{
457#ifdef VISP_HAVE_OPENMP
458#pragma omp parallel for
459#endif
460 for (int i = 0; i < static_cast<int>(normalsImage.getSize()); ++i) {
461 normalDisplayImage.bitmap[i].R = static_cast<unsigned char>((normalsImage.bitmap[i].R + 1.0) * 127.5f);
462 normalDisplayImage.bitmap[i].G = static_cast<unsigned char>((normalsImage.bitmap[i].G + 1.0) * 127.5f);
463 normalDisplayImage.bitmap[i].B = static_cast<unsigned char>((normalsImage.bitmap[i].B + 1.0) * 127.5f);
464 }
465
466 vpDisplay::display(normalDisplayImage);
467 vpDisplay::flush(normalDisplayImage);
468}
469
479void displayCanny(const vpImage<float> &cannyRawData,
480 vpImage<unsigned char> &canny, const vpImage<unsigned char> &valid)
481{
482#ifdef VISP_HAVE_OPENMP
483#pragma omp parallel for
484#endif
485 for (int i = 0; i < static_cast<int>(cannyRawData.getSize()); ++i) {
486 //vpRGBf &px = cannyRawData.bitmap[i];
487 canny.bitmap[i] = valid.bitmap[i] * 255;
488 //canny.bitmap[i] = static_cast<unsigned char>(127.5f + 127.5f * atan(px.B));
489 }
490
491 vpDisplay::display(canny);
492 for (unsigned int i = 0; i < canny.getHeight(); i += 4) {
493 for (unsigned int j = 0; j < canny.getWidth(); j += 4) {
494 if (!valid[i][j]) continue;
495 float angle = cannyRawData[i][j];
496 unsigned x = j + 10 * cos(angle);
497 unsigned y = i + 10 * sin(angle);
498 vpDisplay::displayArrow(canny, i, j, y, x, vpColor::green);
499 }
500 }
501 vpDisplay::flush(canny);
502}
503}
504#endif
505#endif
static const vpColor green
Definition vpColor.h:201
static void display(const vpImage< unsigned char > &I)
static void getImage(const vpImage< unsigned char > &Is, vpImage< vpRGBa > &Id)
static void flush(const vpImage< unsigned char > &I)
static void displayArrow(const vpImage< unsigned char > &I, const vpImagePoint &ip1, const vpImagePoint &ip2, const vpColor &color=vpColor::white, unsigned int w=4, unsigned int h=2, unsigned int thickness=1)
@ badValue
Used to indicate that a value is not in the allowed range.
Definition vpException.h:73
@ dimensionError
Bad dimension.
Definition vpException.h:71
unsigned int getWidth() const
Definition vpImage.h:242
unsigned int getSize() const
Definition vpImage.h:221
Type * bitmap
points toward the bitmap
Definition vpImage.h:135
unsigned int getHeight() const
Definition vpImage.h:181
static void makeDirectory(const std::string &dirname)
static std::string getNameWE(const std::string &pathname)
static std::string getParent(const std::string &pathname)
static const char separator
Definition vpIoTools.h:632
static double deg(double rad)
Definition vpMath.h:119
std::vector< std::shared_ptr< vpDisplay > > makeDisplayGrid(unsigned int rows, unsigned int cols, unsigned int startX, unsigned int startY, unsigned int paddingX, unsigned int paddingY, Args &... args)
Create a grid of displays, given a set of images. All the displays will be initialized in the correct...