Visual Servoing Platform  version 3.6.1 under development (2024-12-04)
photometricVisualServoing.cpp
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 
37 #include <visp3/core/vpConfig.h>
38 #include <visp3/core/vpImage.h>
39 #include <visp3/core/vpImageTools.h>
40 #include <visp3/io/vpImageIo.h>
41 
42 #include <visp3/core/vpCameraParameters.h>
43 #include <visp3/core/vpTime.h>
44 #include <visp3/robot/vpSimulatorCamera.h>
45 
46 #include <visp3/core/vpHomogeneousMatrix.h>
47 #include <visp3/core/vpMath.h>
48 #include <visp3/gui/vpDisplayD3D.h>
49 #include <visp3/gui/vpDisplayGDI.h>
50 #include <visp3/gui/vpDisplayGTK.h>
51 #include <visp3/gui/vpDisplayOpenCV.h>
52 #include <visp3/gui/vpDisplayX.h>
53 
54 #include <visp3/io/vpParseArgv.h>
55 #include <visp3/visual_features/vpFeatureLuminance.h>
56 #include <visp3/vs/vpServo.h>
57 
58 #include <stdlib.h>
59 #include <visp3/robot/vpImageSimulator.h>
60 #define Z 1
61 
62 #include <visp3/core/vpIoTools.h>
63 #include <visp3/io/vpParseArgv.h>
64 
65 // List of allowed command line options
66 #define GETOPTARGS "cdi:n:h"
67 
68 #ifdef ENABLE_VISP_NAMESPACE
69 using namespace VISP_NAMESPACE_NAME;
70 #endif
71 
72 void usage(const char *name, const char *badparam, std::string ipath, int niter);
73 bool getOptions(int argc, const char **argv, std::string &ipath, bool &click_allowed, bool &display, int &niter);
74 
85 void usage(const char *name, const char *badparam, std::string ipath, int niter)
86 {
87  fprintf(stdout, "\n\
88 Tracking of Surf key-points.\n\
89 \n\
90 SYNOPSIS\n\
91  %s [-i <input image path>] [-c] [-d] [-n <number of iterations>] [-h]\n",
92  name);
93 
94  fprintf(stdout, "\n\
95 OPTIONS: Default\n\
96  -i <input image path> %s\n\
97  Set image input path.\n\
98  From this path read \"doisneau/doisneau.jpg\"\n\
99  images. \n\
100  Setting the VISP_INPUT_IMAGE_PATH environment\n\
101  variable produces the same behaviour than using\n\
102  this option.\n\
103 \n\
104  -c\n\
105  Disable the mouse click. Useful to automate the \n\
106  execution of this program without human intervention.\n\
107 \n\
108  -d \n\
109  Turn off the display.\n\
110 \n\
111  -n %%d %d\n\
112  Number of iterations.\n\
113 \n\
114  -h\n\
115  Print the help.\n",
116  ipath.c_str(), niter);
117 
118  if (badparam)
119  fprintf(stdout, "\nERROR: Bad parameter [%s]\n", badparam);
120 }
135 bool getOptions(int argc, const char **argv, std::string &ipath, bool &click_allowed, bool &display, int &niter)
136 {
137  const char *optarg_;
138  int c;
139  while ((c = vpParseArgv::parse(argc, argv, GETOPTARGS, &optarg_)) > 1) {
140 
141  switch (c) {
142  case 'c':
143  click_allowed = false;
144  break;
145  case 'd':
146  display = false;
147  break;
148  case 'i':
149  ipath = optarg_;
150  break;
151  case 'n':
152  niter = atoi(optarg_);
153  break;
154  case 'h':
155  usage(argv[0], nullptr, ipath, niter);
156  return false;
157 
158  default:
159  usage(argv[0], optarg_, ipath, niter);
160  return false;
161  }
162  }
163 
164  if ((c == 1) || (c == -1)) {
165  // standalone param or error
166  usage(argv[0], nullptr, ipath, niter);
167  std::cerr << "ERROR: " << std::endl;
168  std::cerr << " Bad argument " << optarg_ << std::endl << std::endl;
169  return false;
170  }
171 
172  return true;
173 }
174 
175 int main(int argc, const char **argv)
176 {
177 #if (defined(VISP_HAVE_LAPACK) || defined(VISP_HAVE_EIGEN3) || defined(VISP_HAVE_OPENCV))
178  try {
179  std::string env_ipath;
180  std::string opt_ipath;
181  std::string ipath;
182  std::string filename;
183  bool opt_click_allowed = true;
184  bool opt_display = true;
185  int opt_niter = 400;
186 
187  // Get the visp-images-data package path or VISP_INPUT_IMAGE_PATH
188  // environment variable value
189  env_ipath = vpIoTools::getViSPImagesDataPath();
190 
191  // Set the default input path
192  if (!env_ipath.empty())
193  ipath = env_ipath;
194 
195  // Read the command line options
196  if (getOptions(argc, argv, opt_ipath, opt_click_allowed, opt_display, opt_niter) == false) {
197  return EXIT_FAILURE;
198  }
199 
200  // Get the option values
201  if (!opt_ipath.empty())
202  ipath = opt_ipath;
203 
204  // Compare ipath and env_ipath. If they differ, we take into account
205  // the input path coming from the command line option
206  if (!opt_ipath.empty() && !env_ipath.empty()) {
207  if (ipath != env_ipath) {
208  std::cout << std::endl << "WARNING: " << std::endl;
209  std::cout << " Since -i <visp image path=" << ipath << "> "
210  << " is different from VISP_IMAGE_PATH=" << env_ipath << std::endl
211  << " we skip the environment variable." << std::endl;
212  }
213  }
214 
215  // Test if an input path is set
216  if (opt_ipath.empty() && env_ipath.empty()) {
217  usage(argv[0], nullptr, ipath, opt_niter);
218  std::cerr << std::endl << "ERROR:" << std::endl;
219  std::cerr << " Use -i <visp image path> option or set VISP_INPUT_IMAGE_PATH " << std::endl
220  << " environment variable to specify the location of the " << std::endl
221  << " image path where test images are located." << std::endl
222  << std::endl;
223  return EXIT_FAILURE;
224  }
225 
226  vpImage<unsigned char> Itexture;
227  filename = vpIoTools::createFilePath(ipath, "Klimt/Klimt.pgm");
228  vpImageIo::read(Itexture, filename);
229 
230  vpColVector X[4];
231  for (int i = 0; i < 4; i++)
232  X[i].resize(3);
233  // Top left corner
234  X[0][0] = -0.3;
235  X[0][1] = -0.215;
236  X[0][2] = 0;
237 
238  // Top right corner
239  X[1][0] = 0.3;
240  X[1][1] = -0.215;
241  X[1][2] = 0;
242 
243  // Bottom right corner
244  X[2][0] = 0.3;
245  X[2][1] = 0.215;
246  X[2][2] = 0;
247 
248  // Bottom left corner
249  X[3][0] = -0.3;
250  X[3][1] = 0.215;
251  X[3][2] = 0;
252 
253  vpImageSimulator sim;
254 
256  sim.init(Itexture, X);
257 
258  vpCameraParameters cam(870, 870, 160, 120);
259 
260  // ----------------------------------------------------------
261  // Create the framegraber (here a simulated image)
262  vpImage<unsigned char> I(240, 320, 0);
264 
265  // camera desired position
266  vpHomogeneousMatrix cdMo;
267  cdMo[2][3] = 1;
268 
269  // set the robot at the desired position
270  sim.setCameraPosition(cdMo);
271  sim.getImage(I, cam); // and aquire the image Id
272  Id = I;
273 
274  // display the image
275 #if defined(VISP_HAVE_X11)
276  vpDisplayX d;
277 #elif defined(VISP_HAVE_GDI)
278  vpDisplayGDI d;
279 #elif defined(VISP_HAVE_GTK)
280  vpDisplayGTK d;
281 #elif defined(HAVE_OPENCV_HIGHGUI)
282  vpDisplayOpenCV d;
283 #endif
284 
285 #if defined(VISP_HAVE_X11) || defined(VISP_HAVE_GDI) || defined(VISP_HAVE_GTK) || defined(VISP_HAVE_OPENCV)
286  if (opt_display) {
287  d.init(I, 20, 10, "Photometric visual servoing : s");
289  vpDisplay::flush(I);
290  }
291  if (opt_display && opt_click_allowed) {
292  std::cout << "Click in the image to continue..." << std::endl;
294  }
295 #endif
296 
297  // ----------------------------------------------------------
298  // position the robot at the initial position
299  // ----------------------------------------------------------
300 
301  // camera desired position
303  cMo.buildFrom(0, 0, 1.2, vpMath::rad(15), vpMath::rad(-5), vpMath::rad(20));
304  vpHomogeneousMatrix wMo; // Set to identity
305  vpHomogeneousMatrix wMc; // Camera position in the world frame
306 
307  // set the robot at the desired position
308  sim.setCameraPosition(cMo);
309  I = 0u;
310  sim.getImage(I, cam); // and aquire the image Id
311 
312 #if defined(VISP_HAVE_X11) || defined(VISP_HAVE_GDI) || defined(VISP_HAVE_GTK)
313  if (opt_display) {
315  vpDisplay::flush(I);
316  }
317  if (opt_display && opt_click_allowed) {
318  std::cout << "Click in the image to continue..." << std::endl;
320  }
321 #endif
322 
324  Idiff = I;
325 
326  vpImageTools::imageDifference(I, Id, Idiff);
327 
328  // Affiche de l'image de difference
329 #if defined(VISP_HAVE_X11)
330  vpDisplayX d1;
331 #elif defined(VISP_HAVE_GDI)
332  vpDisplayGDI d1;
333 #elif defined(VISP_HAVE_GTK)
334  vpDisplayGTK d1;
335 #endif
336 #if defined(VISP_HAVE_X11) || defined(VISP_HAVE_GDI) || defined(VISP_HAVE_GTK)
337  if (opt_display) {
338  d1.init(Idiff, 40 + static_cast<int>(I.getWidth()), 10, "photometric visual servoing : s-s* ");
339  vpDisplay::display(Idiff);
340  vpDisplay::flush(Idiff);
341  }
342 #endif
343  // create the robot (here a simulated free flying camera)
344  vpSimulatorCamera robot;
345  robot.setSamplingTime(0.04);
346  wMc = wMo * cMo.inverse();
347  robot.setPosition(wMc);
348 
349  // ------------------------------------------------------
350  // Visual feature, interaction matrix, error
351  // s, Ls, Lsd, Lt, Lp, etc
352  // ------------------------------------------------------
353 
354  // current visual feature built from the image
355  // (actually, this is the image...)
357  sI.init(I.getHeight(), I.getWidth(), Z);
358  sI.setCameraParameters(cam);
359  sI.buildFrom(I);
360 
361  // desired visual feature built from the image
362  vpFeatureLuminance sId;
363  sId.init(I.getHeight(), I.getWidth(), Z);
364  sId.setCameraParameters(cam);
365  sId.buildFrom(Id);
366 
367  // Create visual-servoing task
368  vpServo servo;
369  // define the task
370  // - we want an eye-in-hand control law
371  // - robot is controlled in the camera frame
373  // add current and desired visual features
374  servo.addFeature(sI, sId);
375  // set the gain
376  servo.setLambda(30);
377  // compute interaction matrix at the desired position
379 
380  // set a velocity control mode
382 
383  int iter = 1;
384  double normError = 0;
385  vpColVector v; // camera velocity send to the robot
386 
387  vpChrono chrono;
388  chrono.start();
389  do {
390  std::cout << "--------------------------------------------" << iter++ << std::endl;
391 
392  // Acquire the new image
393  sim.setCameraPosition(cMo);
394  sim.getImage(I, cam);
395 #if defined(VISP_HAVE_X11) || defined(VISP_HAVE_GDI) || defined(VISP_HAVE_GTK)
396  if (opt_display) {
398  vpDisplay::flush(I);
399  }
400 #endif
401  vpImageTools::imageDifference(I, Id, Idiff);
402 #if defined(VISP_HAVE_X11) || defined(VISP_HAVE_GDI) || defined(VISP_HAVE_GTK)
403  if (opt_display) {
404  vpDisplay::display(Idiff);
405  vpDisplay::flush(Idiff);
406  }
407 #endif
408  // Compute current visual feature
409  sI.buildFrom(I);
410 
411  v = servo.computeControlLaw(); // camera velocity send to the robot
412 
413  normError = servo.getError().sumSquare();
414  std::cout << " |e| = " << normError << std::endl;
415  std::cout << " |v| = " << sqrt(v.sumSquare()) << std::endl;
416 
417  // send the robot velocity
419  wMc = robot.getPosition();
420  cMo = wMc.inverse() * wMo;
421  } while (normError > 10000 && iter < opt_niter);
422 
423  chrono.stop();
424  std::cout << "Time to convergence: " << chrono.getDurationMs() << " ms" << std::endl;
425 
426  v = 0;
428 
429  return EXIT_SUCCESS;
430  }
431  catch (const vpException &e) {
432  std::cout << "Catch an exception: " << e << std::endl;
433  return EXIT_FAILURE;
434  }
435 #else
436  (void)argc;
437  (void)argv;
438  std::cout << "Cannot run this example: install Lapack, Eigen3 or OpenCV" << std::endl;
439  return EXIT_SUCCESS;
440 #endif
441 }
Generic class defining intrinsic camera parameters.
void start(bool reset=true)
Definition: vpTime.cpp:401
void stop()
Definition: vpTime.cpp:416
double getDurationMs()
Definition: vpTime.cpp:390
Implementation of column vector and the associated operations.
Definition: vpColVector.h:191
double sumSquare() const
Display for windows using GDI (available on any windows 32 platform).
Definition: vpDisplayGDI.h:130
The vpDisplayGTK allows to display image using the GTK 3rd party library. Thus to enable this class G...
Definition: vpDisplayGTK.h:133
void init(vpImage< unsigned char > &I, int win_x=-1, int win_y=-1, const std::string &win_title="") VP_OVERRIDE
The vpDisplayOpenCV allows to display image using the OpenCV library. Thus to enable this class OpenC...
void init(vpImage< unsigned char > &I, int winx=-1, int winy=-1, const std::string &title="") VP_OVERRIDE
static bool getClick(const vpImage< unsigned char > &I, bool blocking=true)
static void display(const vpImage< unsigned char > &I)
static void flush(const vpImage< unsigned char > &I)
error that can be emitted by ViSP classes.
Definition: vpException.h:60
Class that defines the image luminance visual feature.
vpFeatureLuminance & buildFrom(vpImage< unsigned char > &I)
void init(unsigned int _nbr, unsigned int _nbc, double _Z)
void setCameraParameters(const vpCameraParameters &_cam)
Implementation of an homogeneous matrix and operations on such kind of matrices.
vpHomogeneousMatrix & buildFrom(const vpTranslationVector &t, const vpRotationMatrix &R)
vpHomogeneousMatrix inverse() const
static void read(vpImage< unsigned char > &I, const std::string &filename, int backend=IO_DEFAULT_BACKEND)
Definition: vpImageIo.cpp:147
Class which enables to project an image in the 3D space and get the view of a virtual camera.
void getImage(vpImage< unsigned char > &I, const vpCameraParameters &cam)
void init(const vpImage< unsigned char > &I, vpColVector *X)
void setInterpolationType(const vpInterpolationType interplt)
void setCameraPosition(const vpHomogeneousMatrix &cMt)
static void imageDifference(const vpImage< unsigned char > &I1, const vpImage< unsigned char > &I2, vpImage< unsigned char > &Idiff)
unsigned int getWidth() const
Definition: vpImage.h:242
unsigned int getHeight() const
Definition: vpImage.h:181
static std::string getViSPImagesDataPath()
Definition: vpIoTools.cpp:1053
static std::string createFilePath(const std::string &parent, const std::string &child)
Definition: vpIoTools.cpp:1427
static double rad(double deg)
Definition: vpMath.h:129
static bool parse(int *argcPtr, const char **argv, vpArgvInfo *argTable, int flags)
Definition: vpParseArgv.cpp:70
void setVelocity(const vpRobot::vpControlFrameType frame, const vpColVector &vel) VP_OVERRIDE
@ CAMERA_FRAME
Definition: vpRobot.h:84
@ STATE_VELOCITY_CONTROL
Initialize the velocity controller.
Definition: vpRobot.h:67
virtual vpRobotStateType setRobotState(const vpRobot::vpRobotStateType newState)
Definition: vpRobot.cpp:202
void setInteractionMatrixType(const vpServoIteractionMatrixType &interactionMatrixType, const vpServoInversionType &interactionMatrixInversion=PSEUDO_INVERSE)
Definition: vpServo.cpp:380
@ EYEINHAND_CAMERA
Definition: vpServo.h:161
void addFeature(vpBasicFeature &s_cur, vpBasicFeature &s_star, unsigned int select=vpBasicFeature::FEATURE_ALL)
Definition: vpServo.cpp:331
void setLambda(double c)
Definition: vpServo.h:986
void setServo(const vpServoType &servo_type)
Definition: vpServo.cpp:134
vpColVector getError() const
Definition: vpServo.h:510
vpColVector computeControlLaw()
Definition: vpServo.cpp:705
@ CURRENT
Definition: vpServo.h:202
Class that defines the simplest robot: a free flying camera.