Visual Servoing Platform  version 3.1.0
templateTracker.cpp
1 /****************************************************************************
2  *
3  * This file is part of the ViSP software.
4  * Copyright (C) 2005 - 2017 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  * Description:
32  * Example of template tracking.
33  *
34  * Authors:
35  * Amaury Dame
36  * Aurelien Yol
37  * Fabien Spindler
38  *
39  *****************************************************************************/
40 
47 #include <iostream>
48 #include <visp3/core/vpConfig.h>
49 
50 #if defined(VISP_HAVE_MODULE_TT) && defined(VISP_HAVE_DISPLAY)
51 
52 #include <visp3/core/vpDebug.h>
53 #include <visp3/core/vpHomogeneousMatrix.h>
54 #include <visp3/core/vpIoTools.h>
55 #include <visp3/core/vpMath.h>
56 #include <visp3/gui/vpDisplayD3D.h>
57 #include <visp3/gui/vpDisplayGDI.h>
58 #include <visp3/gui/vpDisplayGTK.h>
59 #include <visp3/gui/vpDisplayOpenCV.h>
60 #include <visp3/gui/vpDisplayX.h>
61 #include <visp3/io/vpImageIo.h>
62 #include <visp3/io/vpParseArgv.h>
63 #include <visp3/io/vpVideoReader.h>
64 
65 #include <visp3/tt/vpTemplateTrackerSSD.h>
66 #include <visp3/tt/vpTemplateTrackerSSDESM.h>
67 #include <visp3/tt/vpTemplateTrackerSSDForwardAdditional.h>
68 #include <visp3/tt/vpTemplateTrackerSSDForwardCompositional.h>
69 #include <visp3/tt/vpTemplateTrackerSSDInverseCompositional.h>
70 #include <visp3/tt/vpTemplateTrackerZNCCForwardAdditional.h>
71 #include <visp3/tt/vpTemplateTrackerZNCCInverseCompositional.h>
72 
73 #include <visp3/tt/vpTemplateTrackerWarpAffine.h>
74 #include <visp3/tt/vpTemplateTrackerWarpHomography.h>
75 #include <visp3/tt/vpTemplateTrackerWarpHomographySL3.h>
76 #include <visp3/tt/vpTemplateTrackerWarpRT.h>
77 #include <visp3/tt/vpTemplateTrackerWarpSRT.h>
78 #include <visp3/tt/vpTemplateTrackerWarpTranslation.h>
79 
80 #ifdef VISP_HAVE_MODULE_TT_MI
81 #include <visp3/tt_mi/vpTemplateTrackerMIESM.h>
82 #include <visp3/tt_mi/vpTemplateTrackerMIForwardAdditional.h>
83 #include <visp3/tt_mi/vpTemplateTrackerMIForwardCompositional.h>
84 #include <visp3/tt_mi/vpTemplateTrackerMIInverseCompositional.h>
85 #endif
86 
87 #define GETOPTARGS "cdhi:l:pt:w:"
88 
89 #ifndef DOXYGEN_SHOULD_SKIP_THIS
90 typedef enum {
91  WARP_AFFINE,
92  WARP_HOMOGRAPHY,
93  WARP_HOMOGRAPHY_SL3,
94  WARP_SRT,
95  WARP_TRANSLATION,
96 #ifdef VISP_HAVE_MODULE_TT_MI
97  WARP_RT,
98 #endif
99  WARP_LAST
100 } WarpType;
101 
102 typedef enum {
103  TRACKER_SSD_ESM,
104  TRACKER_SSD_FORWARD_ADDITIONAL,
105  TRACKER_SSD_FORWARD_COMPOSITIONAL,
106  TRACKER_SSD_INVERSE_COMPOSITIONAL, // The most efficient
107  TRACKER_ZNCC_FORWARD_ADDITIONEL,
108  TRACKER_ZNCC_INVERSE_COMPOSITIONAL,
109 #ifdef VISP_HAVE_MODULE_TT_MI
110  TRACKER_MI_ESM,
111  TRACKER_MI_FORWARD_ADDITIONAL,
112  TRACKER_MI_FORWARD_COMPOSITIONAL,
113  TRACKER_MI_INVERSE_COMPOSITIONAL, // The most efficient
114 #endif
115  TRACKER_LAST
116 } TrackerType;
117 
118 #endif
119 
120 void usage(const char *name, const char *badparam, const WarpType &warp_type, TrackerType &tracker_type,
121  const long &last_frame);
122 bool getOptions(int argc, const char **argv, std::string &ipath, bool &click_allowed, bool &display, bool &pyramidal,
123  WarpType &warp_type, TrackerType &tracker_type, long &last_frame);
124 
125 void usage(const char *name, const char *badparam, const WarpType &warp_type, TrackerType &tracker_type,
126  const long &last_frame)
127 {
128  fprintf(stdout, "\n\
129 Example of template tracking.\n\
130 \n\
131 SYNOPSIS\n\
132  %s [-i <test image path>] [-c] [-d] [-p] \n\
133  [-w <warp type>] [-t <tracker type>] \n\
134  [-l <last frame number>] [-h]\n", name);
135 
136  fprintf(stdout, "\n\
137 OPTIONS: Default\n\
138  -i <input image path> \n\
139  Set image input path.\n\
140  From this path read images \n\
141  \"cube/image%%04d.pgm\". These \n\
142  images come from ViSP-images-x.y.z.tar.gz available \n\
143  on the ViSP website.\n\
144  Setting the VISP_INPUT_IMAGE_PATH environment\n\
145  variable produces the same behaviour than using\n\
146  this option.\n\
147  \n\
148  -l <last frame number> %ld\n\
149  Last frame number to consider.\n\
150  \n\
151  -d \n\
152  Turn off the display.\n\
153  \n\
154  -c\n\
155  Disable the mouse click. Useful to automaze the \n\
156  execution of this program without humain intervention.\n\
157  \n", last_frame);
158 
159 #ifdef VISP_HAVE_MODULE_TT_MI
160  fprintf(stdout, "\n\
161  -w <warp type=[0,1,2,3,4,5]> %d\n\
162  Set the model used to warp the template. \n\
163  Authorized values are:\n\
164  %d : Affine\n\
165  %d : Homography\n\
166  %d : Homography in SL3\n\
167  %d : SRT (scale, rotation, translation)\n\
168  %d : RT (rotation, translation)\n\
169  %d : Translation\n\n", (int)warp_type, (int)WARP_AFFINE, (int)WARP_HOMOGRAPHY, (int)WARP_HOMOGRAPHY_SL3, (int)WARP_SRT,
170  (int)WARP_TRANSLATION, (int)WARP_RT);
171 #else
172  fprintf(stdout, "\n\
173  -w <warp type=[0,1,2,3,4]> %d\n\
174  Set the model used to warp the template. \n\
175  Authorized values are:\n\
176  %d : Affine\n\
177  %d : Homography\n\
178  %d : Homography in SL3\n\
179  %d : SRT (scale, rotation, translation)\n\
180  %d : Translation\n\n", (int)warp_type, (int)WARP_AFFINE, (int)WARP_HOMOGRAPHY, (int)WARP_HOMOGRAPHY_SL3, (int)WARP_SRT,
181  (int)WARP_TRANSLATION);
182 #endif
183 
184 #ifdef VISP_HAVE_MODULE_TT_MI
185  fprintf(stdout, "\n\
186  -t <tracker type=[0,1,2,3,4,5,6,7,8,9]> %d\n\
187  Set the tracker used to track the template. \n\
188  Authorized values are:\n\
189  %d : SSD ESM\n\
190  %d : SSD forward additional\n\
191  %d : SSD forward compositional\n\
192  %d : SSD inverse compositional\n\
193  %d : ZNCC forward additional\n\
194  %d : ZNCC inverse compositional\n\
195  %d : MI ESM\n\
196  %d : MI forward additional\n\
197  %d : MI forward compositional\n\
198  %d : MI inverse compositional\n\n", (int)tracker_type, (int)TRACKER_SSD_ESM, (int)TRACKER_SSD_FORWARD_ADDITIONAL,
199  (int)TRACKER_SSD_FORWARD_COMPOSITIONAL, (int)TRACKER_SSD_INVERSE_COMPOSITIONAL,
200  (int)TRACKER_ZNCC_FORWARD_ADDITIONEL, (int)TRACKER_ZNCC_INVERSE_COMPOSITIONAL, (int)TRACKER_MI_ESM,
201  (int)TRACKER_MI_FORWARD_ADDITIONAL, (int)TRACKER_MI_FORWARD_COMPOSITIONAL,
202  (int)TRACKER_MI_INVERSE_COMPOSITIONAL);
203 #else
204  fprintf(stdout, "\n\
205  -t <tracker type=[0,1,2,3,4,5]> %d\n\
206  Set the tracker used to track the template. \n\
207  Authorized values are:\n\
208  %d : SSD ESM\n\
209  %d : SSD forward additional\n\
210  %d : SSD forward compositional\n\
211  %d : SSD inverse compositional\n\
212  %d : ZNCC forward additional\n\
213  %d : ZNCC inverse compositional\n\n", (int)tracker_type, (int)TRACKER_SSD_ESM, (int)TRACKER_SSD_FORWARD_ADDITIONAL,
214  (int)TRACKER_SSD_FORWARD_COMPOSITIONAL, (int)TRACKER_SSD_INVERSE_COMPOSITIONAL,
215  (int)TRACKER_ZNCC_FORWARD_ADDITIONEL, (int)TRACKER_ZNCC_INVERSE_COMPOSITIONAL);
216 
217 #endif
218  fprintf(stdout, "\n\
219  -p\n\
220  Enable pyramidal tracking.\n\
221  \n\
222  -h \n\
223  Print the help.\n\n");
224 
225  if (badparam)
226  fprintf(stdout, "\nERROR: Bad parameter [%s]\n", badparam);
227 }
228 
229 bool getOptions(int argc, const char **argv, std::string &ipath, bool &click_allowed, bool &display, bool &pyramidal,
230  WarpType &warp_type, TrackerType &tracker_type, long &last_frame)
231 {
232  const char *optarg_;
233  int c;
234  while ((c = vpParseArgv::parse(argc, argv, GETOPTARGS, &optarg_)) > 1) {
235 
236  switch (c) {
237  case 'c':
238  click_allowed = false;
239  break;
240  case 'd':
241  display = false;
242  break;
243  case 'h':
244  usage(argv[0], NULL, warp_type, tracker_type, last_frame);
245  return false;
246  break;
247  case 'i':
248  ipath = optarg_;
249  break;
250  case 'l':
251  last_frame = (long)atoi(optarg_);
252  break;
253  case 'p':
254  pyramidal = true;
255  break;
256  case 't':
257  tracker_type = (TrackerType)atoi(optarg_);
258  break;
259  case 'w':
260  warp_type = (WarpType)atoi(optarg_);
261  break;
262 
263  default:
264  usage(argv[0], optarg_, warp_type, tracker_type, last_frame);
265  return false;
266  break;
267  }
268  }
269 
270  if (warp_type >= WARP_LAST) {
271  usage(argv[0], NULL, warp_type, tracker_type, last_frame);
272  std::cerr << "ERROR: " << std::endl;
273  std::cerr << " Bad argument -w <warp type> with \"warp type\"=" << (int)warp_type << std::endl << std::endl;
274  return false;
275  }
276  if (tracker_type >= TRACKER_LAST) {
277  usage(argv[0], NULL, warp_type, tracker_type, last_frame);
278  std::cerr << "ERROR: " << std::endl;
279  std::cerr << " Bad argument -t <tracker type> with \"tracker type\"=" << (int)tracker_type << std::endl
280  << std::endl;
281  return false;
282  }
283  if ((c == 1) || (c == -1)) {
284  // standalone param or error
285  usage(argv[0], NULL, warp_type, tracker_type, last_frame);
286  std::cerr << "ERROR: " << std::endl;
287  std::cerr << " Bad argument " << optarg_ << std::endl << std::endl;
288  return false;
289  }
290 
291  return true;
292 }
293 
294 int main(int argc, const char **argv)
295 {
296  try {
297  std::string env_ipath;
298  std::string opt_ipath;
299  std::string ipath;
300  bool opt_click_allowed = true;
301  bool opt_display = true;
302  bool opt_pyramidal = false;
303  TrackerType opt_tracker_type = TRACKER_SSD_INVERSE_COMPOSITIONAL;
304  WarpType opt_warp_type = WARP_AFFINE;
305  long opt_last_frame = 30;
306 
307  // Get the visp-images-data package path or VISP_INPUT_IMAGE_PATH
308  // environment variable value
309  env_ipath = vpIoTools::getViSPImagesDataPath();
310 
311  // Set the default input path
312  if (!env_ipath.empty())
313  ipath = env_ipath;
314 
315  // Read the command line options
316  if (!getOptions(argc, argv, opt_ipath, opt_click_allowed, opt_display, opt_pyramidal, opt_warp_type,
317  opt_tracker_type, opt_last_frame)) {
318  return (-1);
319  }
320 
321  // Test if an input path is set
322  if (opt_ipath.empty() && env_ipath.empty()) {
323  usage(argv[0], NULL, opt_warp_type, opt_tracker_type, opt_last_frame);
324  std::cerr << std::endl << "ERROR:" << std::endl;
325  std::cerr << " Use -i <visp image path> option or set VISP_INPUT_IMAGE_PATH " << std::endl
326  << " environment variable to specify the location of the " << std::endl
327  << " image path where test images are located." << std::endl
328  << std::endl;
329 
330  return (-1);
331  }
332 
333  // Get the option values
334  if (!opt_ipath.empty())
335  ipath = vpIoTools::createFilePath(opt_ipath, "mire-2/image.%04d.pgm");
336  else
337  ipath = vpIoTools::createFilePath(env_ipath, "mire-2/image.%04d.pgm");
338 
340  vpVideoReader reader;
341 
342  reader.setFileName(ipath.c_str());
343  reader.setFirstFrameIndex(1);
344  reader.setLastFrameIndex(opt_last_frame);
345  try {
346  reader.open(I);
347  } catch (...) {
348  std::cout << "Cannot open sequence: " << ipath << std::endl;
349  return -1;
350  }
351  reader.acquire(I);
352 
353  vpDisplay *display = NULL;
354  if (opt_display) {
355 // initialise a display
356 #if defined VISP_HAVE_X11
357  display = new vpDisplayX;
358 #elif defined VISP_HAVE_GDI
359  display = new vpDisplayGDI;
360 #elif defined VISP_HAVE_OPENCV
361  display = new vpDisplayOpenCV;
362 #elif defined VISP_HAVE_D3D9
363  display = new vpDisplayD3D;
364 #elif defined VISP_HAVE_GTK
365  display = new vpDisplayGTK;
366 #else
367  opt_display = false;
368 #endif
369 #if (defined VISP_HAVE_DISPLAY)
370  display->init(I, 100, 100, "Test tracking");
371 #endif
373  vpDisplay::flush(I);
374  }
375 
376  vpTemplateTrackerWarp *warp = NULL;
377  switch (opt_warp_type) {
378  case WARP_AFFINE:
379  warp = new vpTemplateTrackerWarpAffine;
380  break;
381  case WARP_HOMOGRAPHY:
383  break;
384  case WARP_HOMOGRAPHY_SL3:
386  break;
387  case WARP_SRT:
388  warp = new vpTemplateTrackerWarpSRT;
389  break;
390  case WARP_TRANSLATION:
392  break;
393 #ifdef VISP_HAVE_MODULE_TT_MI
394  case WARP_RT:
395  warp = new vpTemplateTrackerWarpRT;
396  break;
397 #endif
398  default:
399  return 0;
400  }
401 
402  vpTemplateTracker *tracker = NULL;
403  switch (opt_tracker_type) {
404  case TRACKER_SSD_ESM:
405  tracker = new vpTemplateTrackerSSDESM(warp);
406  break;
407  case TRACKER_SSD_FORWARD_ADDITIONAL:
408  tracker = new vpTemplateTrackerSSDForwardAdditional(warp);
409  break;
410  case TRACKER_SSD_FORWARD_COMPOSITIONAL:
411  tracker = new vpTemplateTrackerSSDForwardCompositional(warp);
412  break;
413  case TRACKER_SSD_INVERSE_COMPOSITIONAL:
414  tracker = new vpTemplateTrackerSSDInverseCompositional(warp);
415  break;
416  case TRACKER_ZNCC_FORWARD_ADDITIONEL:
417  tracker = new vpTemplateTrackerZNCCForwardAdditional(warp);
418  break;
419  case TRACKER_ZNCC_INVERSE_COMPOSITIONAL:
420  tracker = new vpTemplateTrackerZNCCInverseCompositional(warp);
421  break;
422 #ifdef VISP_HAVE_MODULE_TT_MI
423  case TRACKER_MI_ESM:
424  tracker = new vpTemplateTrackerMIESM(warp);
425  break;
426  case TRACKER_MI_FORWARD_ADDITIONAL:
427  tracker = new vpTemplateTrackerMIForwardAdditional(warp);
428  break;
429  case TRACKER_MI_FORWARD_COMPOSITIONAL:
430  tracker = new vpTemplateTrackerMIForwardCompositional(warp);
431  break;
432  case TRACKER_MI_INVERSE_COMPOSITIONAL:
433  tracker = new vpTemplateTrackerMIInverseCompositional(warp);
434  break;
435 #endif
436  default:
437  return 0;
438  }
439 
440  tracker->setSampling(2, 2);
441  tracker->setLambda(0.001);
442  tracker->setThresholdGradient(60.);
443  tracker->setIterationMax(800);
444  if (opt_pyramidal) {
445  tracker->setPyramidal(2, 1);
446  }
447  bool delaunay = false;
448  if (opt_display && opt_click_allowed)
449  tracker->initClick(I, delaunay);
450  else {
451  std::vector<vpImagePoint> v_ip;
452  vpImagePoint ip;
453  ip.set_ij(166, 54);
454  v_ip.push_back(ip);
455  ip.set_ij(284, 55);
456  v_ip.push_back(ip);
457  ip.set_ij(259, 284);
458  v_ip.push_back(ip); // ends the first triangle
459  ip.set_ij(259, 284);
460  v_ip.push_back(ip); // start the second triangle
461  ip.set_ij(149, 240);
462  v_ip.push_back(ip);
463  ip.set_ij(167, 58);
464  v_ip.push_back(ip);
465 
466  tracker->initFromPoints(I, v_ip, false);
467  }
468 
469  while (!reader.end()) {
470  // Acquire a new image
471  reader.acquire(I);
472  std::cout << "Process image number " << reader.getFrameIndex() << std::endl;
473  // Display the image
475  // Track the template
476  tracker->track(I);
477 
478  // Simulate a re-init
479  if (reader.getFrameIndex() == 10) {
480  std::cout << "re-init simulation" << std::endl;
481  if (opt_click_allowed)
483 
484  tracker->resetTracker();
485 
486  if (opt_display && opt_click_allowed) {
487  vpDisplay::displayText(I, 10, 10, "Re-init simulation", vpColor::red);
488  vpDisplay::flush(I);
489  tracker->initClick(I, delaunay);
490  } else {
491  std::vector<vpImagePoint> v_ip;
492  vpImagePoint ip;
493  ip.set_ij(146, 60);
494  v_ip.push_back(ip);
495  ip.set_ij(254, 74);
496  v_ip.push_back(ip);
497  ip.set_ij(228, 288);
498  v_ip.push_back(ip); // ends the first triangle
499  ip.set_ij(228, 288);
500  v_ip.push_back(ip); // start the second triangle
501  ip.set_ij(126, 242);
502  v_ip.push_back(ip);
503  ip.set_ij(146, 60);
504  v_ip.push_back(ip);
505 
506  tracker->initFromPoints(I, v_ip, false);
507  }
508  }
509 
510 // Display the template
511 #if 1
512  tracker->display(I, vpColor::red, 3);
513 #else
514  vpTemplateTrackerZone zoneWarped_, zoneRef_ = tracker->getZoneRef();
515  vpTemplateTrackerWarp *warp_ = tracker->getWarp();
516  vpColVector p_ = tracker->getp();
517  warp_->warpZone(zoneRef_, p_, zoneWarped_);
518  zoneWarped_.display(I, vpColor::red, 3);
519  zoneRef_.display(I, vpColor::green, 3);
520 #endif
521 
522  vpDisplay::flush(I);
523  }
524  if (opt_click_allowed) {
525  vpDisplay::displayText(I, 10, 10, "A click to exit...", vpColor::red);
526  vpDisplay::flush(I);
528  }
529  reader.close();
530  if (display)
531  delete display;
532 
533  delete warp;
534  delete tracker;
535 
536  return 0;
537  } catch (vpException &e) {
538  std::cout << "Catch an exception: " << e << std::endl;
539  return -1;
540  }
541 }
542 
543 #else
544 
545 int main()
546 {
547  std::cout << "visp_tt module or display not available." << std::endl;
548  return 0;
549 }
550 
551 #endif
void setSampling(int sample_i, int sample_j)
Class that defines generic functionnalities for display.
Definition: vpDisplay.h:171
static bool getClick(const vpImage< unsigned char > &I, bool blocking=true)
static std::string getViSPImagesDataPath()
Definition: vpIoTools.cpp:1210
Display for windows using GDI (available on any windows 32 platform).
Definition: vpDisplayGDI.h:129
static void displayText(const vpImage< unsigned char > &I, const vpImagePoint &ip, const std::string &s, const vpColor &color)
Use the X11 console to display images on unix-like OS. Thus to enable this class X11 should be instal...
Definition: vpDisplayX.h:151
Class that enables to manipulate easily a video file or a sequence of images. As it inherits from the...
error that can be emited by ViSP classes.
Definition: vpException.h:71
void display(const vpImage< unsigned char > &I, const vpColor &col=vpColor::green, const unsigned int thickness=3)
void setIterationMax(const unsigned int &n)
static const vpColor green
Definition: vpColor.h:183
static void flush(const vpImage< unsigned char > &I)
static bool parse(int *argcPtr, const char **argv, vpArgvInfo *argTable, int flags)
Definition: vpParseArgv.cpp:69
static const vpColor red
Definition: vpColor.h:180
vpColVector getp() const
void open(vpImage< vpRGBa > &I)
Display for windows using Direct3D 3rd party. Thus to enable this class Direct3D should be installed...
Definition: vpDisplayD3D.h:107
static std::string createFilePath(const std::string &parent, const std::string &child)
Definition: vpIoTools.cpp:1435
static void display(const vpImage< unsigned char > &I)
The vpDisplayOpenCV allows to display image using the OpenCV library. Thus to enable this class OpenC...
vpTemplateTrackerWarp * getWarp() const
void initClick(const vpImage< unsigned char > &I, bool delaunay=false)
The vpDisplayGTK allows to display image using the GTK 3rd party library. Thus to enable this class G...
Definition: vpDisplayGTK.h:138
void acquire(vpImage< vpRGBa > &I)
void setFileName(const char *filename)
void initFromPoints(const vpImage< unsigned char > &I, const std::vector< vpImagePoint > &v_ip, bool delaunay=false)
void warpZone(const vpTemplateTrackerZone &in, const vpColVector &p, vpTemplateTrackerZone &out)
long getFrameIndex() const
void setLastFrameIndex(const long last_frame)
Implementation of column vector and the associated operations.
Definition: vpColVector.h:72
void setLambda(double l)
void track(const vpImage< unsigned char > &I)
void setFirstFrameIndex(const long first_frame)
Class that defines a 2D point in an image. This class is useful for image processing and stores only ...
Definition: vpImagePoint.h:88
vpTemplateTrackerZone getZoneRef() const
void set_ij(const double ii, const double jj)
Definition: vpImagePoint.h:189
void setPyramidal(unsigned int nlevels=2, unsigned int level_to_stop=1)
void setThresholdGradient(double threshold)
void display(const vpImage< unsigned char > &I, const vpColor &col=vpColor::green, const unsigned int thickness=3)