Visual Servoing Platform  version 3.3.0 under development (2020-02-17)
photometricVisualServoing.cpp
1 /****************************************************************************
2  *
3  * ViSP, open source Visual Servoing Platform software.
4  * Copyright (C) 2005 - 2019 by Inria. All rights reserved.
5  *
6  * This software is free software; you can redistribute it and/or modify
7  * it under the terms of the GNU General Public License as published by
8  * the Free Software Foundation; either version 2 of the License, or
9  * (at your option) any later version.
10  * See the file LICENSE.txt at the root directory of this source
11  * distribution for additional information about the GNU GPL.
12  *
13  * For using ViSP with software that can not be combined with the GNU
14  * GPL, please contact Inria about acquiring a ViSP Professional
15  * Edition License.
16  *
17  * See http://visp.inria.fr for more information.
18  *
19  * This software was developed at:
20  * Inria Rennes - Bretagne Atlantique
21  * Campus Universitaire de Beaulieu
22  * 35042 Rennes Cedex
23  * France
24  *
25  * If you have questions regarding the use of this file, please contact
26  * Inria at visp@inria.fr
27  *
28  * This file is provided AS IS with NO WARRANTY OF ANY KIND, INCLUDING THE
29  * WARRANTY OF DESIGN, MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE.
30  *
31  * Authors:
32  * Eric Marchand
33  * Christophe Collewet
34  *
35  *****************************************************************************/
36 
43 #include <visp3/core/vpDebug.h>
44 
45 #include <visp3/core/vpImage.h>
46 #include <visp3/core/vpImageTools.h>
47 #include <visp3/io/vpImageIo.h>
48 
49 #include <visp3/core/vpCameraParameters.h>
50 #include <visp3/core/vpTime.h>
51 #include <visp3/robot/vpSimulatorCamera.h>
52 
53 #include <visp3/core/vpHomogeneousMatrix.h>
54 #include <visp3/core/vpMath.h>
55 #include <visp3/gui/vpDisplayD3D.h>
56 #include <visp3/gui/vpDisplayGDI.h>
57 #include <visp3/gui/vpDisplayGTK.h>
58 #include <visp3/gui/vpDisplayOpenCV.h>
59 #include <visp3/gui/vpDisplayX.h>
60 
61 #include <visp3/io/vpParseArgv.h>
62 #include <visp3/visual_features/vpFeatureLuminance.h>
63 
64 #include <stdlib.h>
65 #include <visp3/robot/vpImageSimulator.h>
66 #define Z 1
67 
68 #include <visp3/core/vpIoTools.h>
69 #include <visp3/io/vpParseArgv.h>
70 
71 // List of allowed command line options
72 #define GETOPTARGS "cdi:n:h"
73 
74 void usage(const char *name, const char *badparam, std::string ipath, int niter);
75 bool getOptions(int argc, const char **argv, std::string &ipath, bool &click_allowed, bool &display, int &niter);
76 
87 void usage(const char *name, const char *badparam, std::string ipath, int niter)
88 {
89  fprintf(stdout, "\n\
90 Tracking of Surf key-points.\n\
91 \n\
92 SYNOPSIS\n\
93  %s [-i <input image path>] [-c] [-d] [-n <number of iterations>] [-h]\n", name);
94 
95  fprintf(stdout, "\n\
96 OPTIONS: Default\n\
97  -i <input image path> %s\n\
98  Set image input path.\n\
99  From this path read \"doisneau/doisneau.jpg\"\n\
100  images. \n\
101  Setting the VISP_INPUT_IMAGE_PATH environment\n\
102  variable produces the same behaviour than using\n\
103  this option.\n\
104 \n\
105  -c\n\
106  Disable the mouse click. Useful to automaze the \n\
107  execution of this program without humain intervention.\n\
108 \n\
109  -d \n\
110  Turn off the display.\n\
111 \n\
112  -n %%d %d\n\
113  Number of iterations.\n\
114 \n\
115  -h\n\
116  Print the help.\n", 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], NULL, ipath, niter);
156  return false;
157  break;
158 
159  default:
160  usage(argv[0], optarg_, ipath, niter);
161  return false;
162  break;
163  }
164  }
165 
166  if ((c == 1) || (c == -1)) {
167  // standalone param or error
168  usage(argv[0], NULL, ipath, niter);
169  std::cerr << "ERROR: " << std::endl;
170  std::cerr << " Bad argument " << optarg_ << std::endl << std::endl;
171  return false;
172  }
173 
174  return true;
175 }
176 
177 int main(int argc, const char **argv)
178 {
179  try {
180  std::string env_ipath;
181  std::string opt_ipath;
182  std::string ipath;
183  std::string filename;
184  bool opt_click_allowed = true;
185  bool opt_display = true;
186  int opt_niter = 400;
187 
188  // Get the visp-images-data package path or VISP_INPUT_IMAGE_PATH
189  // environment variable value
190  env_ipath = vpIoTools::getViSPImagesDataPath();
191 
192  // Set the default input path
193  if (!env_ipath.empty())
194  ipath = env_ipath;
195 
196  // Read the command line options
197  if (getOptions(argc, argv, opt_ipath, opt_click_allowed, opt_display, opt_niter) == false) {
198  return (-1);
199  }
200 
201  // Get the option values
202  if (!opt_ipath.empty())
203  ipath = opt_ipath;
204 
205  // Compare ipath and env_ipath. If they differ, we take into account
206  // the input path comming from the command line option
207  if (!opt_ipath.empty() && !env_ipath.empty()) {
208  if (ipath != env_ipath) {
209  std::cout << std::endl << "WARNING: " << std::endl;
210  std::cout << " Since -i <visp image path=" << ipath << "> "
211  << " is different from VISP_IMAGE_PATH=" << env_ipath << std::endl
212  << " we skip the environment variable." << std::endl;
213  }
214  }
215 
216  // Test if an input path is set
217  if (opt_ipath.empty() && env_ipath.empty()) {
218  usage(argv[0], NULL, ipath, opt_niter);
219  std::cerr << std::endl << "ERROR:" << std::endl;
220  std::cerr << " Use -i <visp image path> option or set VISP_INPUT_IMAGE_PATH " << std::endl
221  << " environment variable to specify the location of the " << std::endl
222  << " image path where test images are located." << std::endl
223  << std::endl;
224  exit(-1);
225  }
226 
227  vpImage<unsigned char> Itexture;
228  filename = vpIoTools::createFilePath(ipath, "Klimt/Klimt.pgm");
229  vpImageIo::read(Itexture, filename);
230 
231  vpColVector X[4];
232  for (int i = 0; i < 4; i++)
233  X[i].resize(3);
234  // Top left corner
235  X[0][0] = -0.3;
236  X[0][1] = -0.215;
237  X[0][2] = 0;
238 
239  // Top right corner
240  X[1][0] = 0.3;
241  X[1][1] = -0.215;
242  X[1][2] = 0;
243 
244  // Bottom right corner
245  X[2][0] = 0.3;
246  X[2][1] = 0.215;
247  X[2][2] = 0;
248 
249  // Bottom left corner
250  X[3][0] = -0.3;
251  X[3][1] = 0.215;
252  X[3][2] = 0;
253 
254  vpImageSimulator sim;
255 
257  sim.init(Itexture, X);
258 
259  vpCameraParameters cam(870, 870, 160, 120);
260 
261  // ----------------------------------------------------------
262  // Create the framegraber (here a simulated image)
263  vpImage<unsigned char> I(240, 320, 0);
265 
266  // camera desired position
267  vpHomogeneousMatrix cdMo;
268  cdMo[2][3] = 1;
269 
270  // set the robot at the desired position
271  sim.setCameraPosition(cdMo);
272  sim.getImage(I, cam); // and aquire the image Id
273  Id = I;
274 
275 // display the image
276 #if defined VISP_HAVE_X11
277  vpDisplayX d;
278 #elif defined VISP_HAVE_GDI
279  vpDisplayGDI d;
280 #elif defined VISP_HAVE_GTK
281  vpDisplayGTK d;
282 #elif defined VISP_HAVE_OPENCV
283  vpDisplayOpenCV d;
284 #endif
285 
286 #if defined(VISP_HAVE_X11) || defined(VISP_HAVE_GDI) || defined(VISP_HAVE_GTK) || defined(VISP_HAVE_OPENCV)
287  if (opt_display) {
288  d.init(I, 20, 10, "Photometric visual servoing : s");
290  vpDisplay::flush(I);
291  }
292  if (opt_display && opt_click_allowed) {
293  std::cout << "Click in the image to continue..." << std::endl;
295  }
296 #endif
297 
298  // ----------------------------------------------------------
299  // position the robot at the initial position
300  // ----------------------------------------------------------
301 
302  // camera desired position
304  cMo.buildFrom(0, 0, 1.2, vpMath::rad(15), vpMath::rad(-5), vpMath::rad(20));
305  vpHomogeneousMatrix wMo; // Set to identity
306  vpHomogeneousMatrix wMc; // Camera position in the world frame
307 
308  // set the robot at the desired position
309  sim.setCameraPosition(cMo);
310  I = 0;
311  sim.getImage(I, cam); // and aquire the image Id
312 
313 #if defined(VISP_HAVE_X11) || defined(VISP_HAVE_GDI) || defined(VISP_HAVE_GTK)
314  if (opt_display) {
316  vpDisplay::flush(I);
317  }
318  if (opt_display && opt_click_allowed) {
319  std::cout << "Click in the image to continue..." << std::endl;
321  }
322 #endif
323 
325  Idiff = I;
326 
327  vpImageTools::imageDifference(I, Id, Idiff);
328 
329 // Affiche de l'image de difference
330 #if defined VISP_HAVE_X11
331  vpDisplayX d1;
332 #elif defined VISP_HAVE_GDI
333  vpDisplayGDI d1;
334 #elif defined VISP_HAVE_GTK
335  vpDisplayGTK d1;
336 #endif
337 #if defined(VISP_HAVE_X11) || defined(VISP_HAVE_GDI) || defined(VISP_HAVE_GTK)
338  if (opt_display) {
339  d1.init(Idiff, 40 + (int)I.getWidth(), 10, "photometric visual servoing : s-s* ");
340  vpDisplay::display(Idiff);
341  vpDisplay::flush(Idiff);
342  }
343 #endif
344  // create the robot (here a simulated free flying camera)
345  vpSimulatorCamera robot;
346  robot.setSamplingTime(0.04);
347  wMc = wMo * cMo.inverse();
348  robot.setPosition(wMc);
349 
350  // ------------------------------------------------------
351  // Visual feature, interaction matrix, error
352  // s, Ls, Lsd, Lt, Lp, etc
353  // ------------------------------------------------------
354 
355  // current visual feature built from the image
356  // (actually, this is the image...)
358  sI.init(I.getHeight(), I.getWidth(), Z);
359  sI.setCameraParameters(cam);
360  sI.buildFrom(I);
361 
362  // desired visual feature built from the image
363  vpFeatureLuminance sId;
364  sId.init(I.getHeight(), I.getWidth(), Z);
365  sId.setCameraParameters(cam);
366  sId.buildFrom(Id);
367 
368  // Matrice d'interaction, Hessien, erreur,...
369  vpMatrix Lsd; // matrice d'interaction a la position desiree
370  vpMatrix Hsd; // hessien a la position desiree
371  vpMatrix H; // Hessien utilise pour le levenberg-Marquartd
372  vpColVector error; // Erreur I-I*
373 
374  // Compute the interaction matrix
375  // link the variation of image intensity to camera motion
376 
377  // here it is computed at the desired position
378  sId.interaction(Lsd);
379 
380  // Compute the Hessian H = L^TL
381  Hsd = Lsd.AtA();
382 
383  // Compute the Hessian diagonal for the Levenberg-Marquartd
384  // optimization process
385  unsigned int n = 6;
386  vpMatrix diagHsd(n, n);
387  diagHsd.eye(n);
388  for (unsigned int i = 0; i < n; i++)
389  diagHsd[i][i] = Hsd[i][i];
390 
391  // ------------------------------------------------------
392  // Control law
393  double lambda; // gain
394  vpColVector e;
395  vpColVector v; // camera velocity send to the robot
396 
397  // ----------------------------------------------------------
398  // Minimisation
399 
400  double mu; // mu = 0 : Gauss Newton ; mu != 0 : LM
401  double lambdaGN;
402 
403  mu = 0.01;
404  lambda = 30;
405  lambdaGN = 30;
406 
407  // set a velocity control mode
409 
410  // ----------------------------------------------------------
411  int iter = 1;
412  int iterGN = 90; // swicth to Gauss Newton after iterGN iterations
413 
414  double normeError = 0;
415  do {
416  std::cout << "--------------------------------------------" << iter++ << std::endl;
417 
418  // Acquire the new image
419  sim.setCameraPosition(cMo);
420  sim.getImage(I, cam);
421 #if defined(VISP_HAVE_X11) || defined(VISP_HAVE_GDI) || defined(VISP_HAVE_GTK)
422  if (opt_display) {
424  vpDisplay::flush(I);
425  }
426 #endif
427  vpImageTools::imageDifference(I, Id, Idiff);
428 #if defined(VISP_HAVE_X11) || defined(VISP_HAVE_GDI) || defined(VISP_HAVE_GTK)
429  if (opt_display) {
430  vpDisplay::display(Idiff);
431  vpDisplay::flush(Idiff);
432  }
433 #endif
434  // Compute current visual feature
435  sI.buildFrom(I);
436 
437  // compute current error
438  sI.error(sId, error);
439 
440  normeError = (error.sumSquare());
441  std::cout << "|e| " << normeError << std::endl;
442 
443  // double t = vpTime::measureTimeMs() ;
444 
445  // ---------- Levenberg Marquardt method --------------
446  {
447  if (iter > iterGN) {
448  mu = 0.0001;
449  lambda = lambdaGN;
450  }
451 
452  // Compute the levenberg Marquartd term
453  {
454  H = ((mu * diagHsd) + Hsd).inverseByLU();
455  }
456  // compute the control law
457  e = H * Lsd.t() * error;
458 
459  v = -lambda * e;
460  }
461 
462  std::cout << "lambda = " << lambda << " mu = " << mu;
463  std::cout << " |Tc| = " << sqrt(v.sumSquare()) << std::endl;
464 
465  // send the robot velocity
467  wMc = robot.getPosition();
468  cMo = wMc.inverse() * wMo;
469  } while (normeError > 10000 && iter < opt_niter);
470 
471  v = 0;
473 
474  return EXIT_SUCCESS;
475  } catch (const vpException &e) {
476  std::cout << "Catch an exception: " << e << std::endl;
477  return EXIT_FAILURE;
478  }
479 }
void setPosition(const vpHomogeneousMatrix &wMc)
Implementation of a matrix and operations on matrices.
Definition: vpMatrix.h:164
void init(const vpImage< unsigned char > &I, vpColVector *X)
static bool getClick(const vpImage< unsigned char > &I, bool blocking=true)
void setVelocity(const vpRobot::vpControlFrameType frame, const vpColVector &vel)
static std::string getViSPImagesDataPath()
Definition: vpIoTools.cpp:1292
Implementation of an homogeneous matrix and operations on such kind of matrices.
Class that defines the simplest robot: a free flying camera.
void buildFrom(vpImage< unsigned char > &I)
void getImage(vpImage< unsigned char > &I, const vpCameraParameters &cam)
vpMatrix AtA() const
Definition: vpMatrix.cpp:693
Display for windows using GDI (available on any windows 32 platform).
Definition: vpDisplayGDI.h:128
void setCameraParameters(vpCameraParameters &_cam)
Use the X11 console to display images on unix-like OS. Thus to enable this class X11 should be instal...
Definition: vpDisplayX.h:150
vpMatrix inverseByLU() const
error that can be emited by ViSP classes.
Definition: vpException.h:71
void init(vpImage< unsigned char > &I, int winx=-1, int winy=-1, const std::string &title="")
vpHomogeneousMatrix inverse() const
vpHomogeneousMatrix getPosition() const
static void imageDifference(const vpImage< unsigned char > &I1, const vpImage< unsigned char > &I2, vpImage< unsigned char > &Idiff)
static void flush(const vpImage< unsigned char > &I)
static bool parse(int *argcPtr, const char **argv, vpArgvInfo *argTable, int flags)
Definition: vpParseArgv.cpp:69
virtual vpRobotStateType setRobotState(const vpRobot::vpRobotStateType newState)
Definition: vpRobot.cpp:201
vpMatrix interaction(unsigned int select=FEATURE_ALL)
virtual void setSamplingTime(const double &delta_t)
vpMatrix t() const
Definition: vpMatrix.cpp:507
void setCameraPosition(const vpHomogeneousMatrix &cMt)
Initialize the velocity controller.
Definition: vpRobot.h:66
static std::string createFilePath(const std::string &parent, const std::string &child)
Definition: vpIoTools.cpp:1537
void setInterpolationType(const vpInterpolationType interplt)
Class that defines the image luminance visual feature.
vpColVector error(const vpBasicFeature &s_star, unsigned int select=FEATURE_ALL)
static void display(const vpImage< unsigned char > &I)
The vpDisplayOpenCV allows to display image using the OpenCV library. Thus to enable this class OpenC...
Generic class defining intrinsic camera parameters.
Class which enables to project an image in the 3D space and get the view of a virtual camera...
The vpDisplayGTK allows to display image using the GTK 3rd party library. Thus to enable this class G...
Definition: vpDisplayGTK.h:137
void buildFrom(const vpTranslationVector &t, const vpRotationMatrix &R)
static double rad(double deg)
Definition: vpMath.h:108
void init(vpImage< unsigned char > &I, int winx=-1, int winy=-1, const std::string &title="")
unsigned int getHeight() const
Definition: vpImage.h:186
static void read(vpImage< unsigned char > &I, const std::string &filename)
Definition: vpImageIo.cpp:243
Implementation of column vector and the associated operations.
Definition: vpColVector.h:130
double sumSquare() const
unsigned int getWidth() const
Definition: vpImage.h:244