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 * Description:
32 * Generic model based tracker
33 *
34 * Authors:
35 * Romain Tallonneau
36 * Aurelien Yol
37 * Eric Marchand
38 *
39 *****************************************************************************/
40
41 /*!
42 \file vpMbTracker.cpp
43 \brief Generic model based tracker
44 */
45
46 #include <algorithm>
47 #include <iostream>
48 #include <limits>
49
50 #include <Simd/SimdLib.hpp>
51
52 #include <visp3/core/vpColVector.h>
53 #include <visp3/core/vpDisplay.h>
54 #include <visp3/core/vpMath.h>
55 #include <visp3/core/vpMatrix.h>
56 #include <visp3/core/vpPoint.h>
57 #include <visp3/vision/vpPose.h>
58 #ifdef VISP_HAVE_MODULE_GUI
59 #include <visp3/gui/vpDisplayGDI.h>
60 #include <visp3/gui/vpDisplayOpenCV.h>
61 #include <visp3/gui/vpDisplayX.h>
62 #endif
63 #include <visp3/core/vpCameraParameters.h>
64 #include <visp3/core/vpColor.h>
65 #include <visp3/core/vpException.h>
66 #include <visp3/core/vpIoTools.h>
67 #include <visp3/core/vpPixelMeterConversion.h>
68 #ifdef VISP_HAVE_MODULE_IO
69 #include <visp3/io/vpImageIo.h>
70 #endif
71 #include <visp3/core/vpCPUFeatures.h>
72 #include <visp3/core/vpIoTools.h>
73 #include <visp3/core/vpMatrixException.h>
74 #include <visp3/core/vpTrackingException.h>
75 #include <visp3/mbt/vpMbTracker.h>
76
77 #include <visp3/core/vpImageFilter.h>
78 #include <visp3/mbt/vpMbtXmlGenericParser.h>
79
80 #ifdef VISP_HAVE_COIN3D
81 // Inventor includes
82 #include <Inventor/VRMLnodes/SoVRMLCoordinate.h>
83 #include <Inventor/VRMLnodes/SoVRMLGroup.h>
84 #include <Inventor/VRMLnodes/SoVRMLIndexedFaceSet.h>
85 #include <Inventor/VRMLnodes/SoVRMLIndexedLineSet.h>
86 #include <Inventor/VRMLnodes/SoVRMLShape.h>
87 #include <Inventor/VRMLnodes/SoVRMLTransform.h>
88 #include <Inventor/actions/SoGetMatrixAction.h>
89 #include <Inventor/actions/SoGetPrimitiveCountAction.h>
90 #include <Inventor/actions/SoSearchAction.h>
91 #include <Inventor/actions/SoToVRML2Action.h>
92 #include <Inventor/actions/SoWriteAction.h>
93 #include <Inventor/misc/SoChildList.h>
94 #include <Inventor/nodes/SoSeparator.h>
95 #endif
96
97 #ifndef DOXYGEN_SHOULD_SKIP_THIS
98
99 namespace
100 {
101 /*!
102 Structure to store info about segment in CAO model files.
103 */
104 struct SegmentInfo {
SegmentInfo__anon107a05a50111::SegmentInfo105 SegmentInfo() : extremities(), name(), useLod(false), minLineLengthThresh(0.) {}
106
107 std::vector<vpPoint> extremities;
108 std::string name;
109 bool useLod;
110 double minLineLengthThresh;
111 };
112
113 /*!
114 Structure to store info about a polygon face represented by a vpPolygon and
115 by a list of vpPoint representing the corners of the polygon face in 3D.
116 */
117 struct PolygonFaceInfo {
PolygonFaceInfo__anon107a05a50111::PolygonFaceInfo118 PolygonFaceInfo(double dist, const vpPolygon &poly, const std::vector<vpPoint> &corners)
119 : distanceToCamera(dist), polygon(poly), faceCorners(corners)
120 {
121 }
122
operator <__anon107a05a50111::PolygonFaceInfo123 bool operator<(const PolygonFaceInfo &pfi) const { return distanceToCamera < pfi.distanceToCamera; }
124
125 double distanceToCamera;
126 vpPolygon polygon;
127 std::vector<vpPoint> faceCorners;
128 };
129
130 /*!
131 Reference: https://stackoverflow.com/a/6089413
132 To replace the following old code:
133 char buffer[256];
134 fileId.getline(buffer, 256);
135 This should take care of the different line ending styles.
136 */
safeGetline(std::istream & is,std::string & t)137 std::istream& safeGetline(std::istream& is, std::string& t)
138 {
139 t.clear();
140
141 // The characters in the stream are read one-by-one using a std::streambuf.
142 // That is faster than reading them one-by-one using the std::istream.
143 // Code that uses streambuf this way must be guarded by a sentry object.
144 // The sentry object performs various tasks,
145 // such as thread synchronization and updating the stream state.
146
147 std::istream::sentry se(is, true);
148 std::streambuf* sb = is.rdbuf();
149
150 for(;;) {
151 int c = sb->sbumpc();
152 if (c == '\n') {
153 return is;
154 }
155 else if (c == '\r') {
156 if(sb->sgetc() == '\n')
157 sb->sbumpc();
158 return is;
159 }
160 else if (c == std::streambuf::traits_type::eof()) {
161 // Also handle the case when the last line has no line ending
162 if(t.empty())
163 is.setstate(std::ios::eofbit);
164 return is;
165 }
166 else { // default case
167 t += (char)c;
168 }
169 }
170 }
171 }
172 #endif // DOXYGEN_SHOULD_SKIP_THIS
173
174 /*!
175 Basic constructor.
176 Set default values.
177
178 */
vpMbTracker()179 vpMbTracker::vpMbTracker()
180 : m_cam(), m_cMo(), oJo(6, 6), isoJoIdentity(true), modelFileName(), modelInitialised(false), poseSavingFilename(),
181 computeCovariance(false), covarianceMatrix(), computeProjError(false), projectionError(90.0),
182 displayFeatures(false), m_optimizationMethod(vpMbTracker::GAUSS_NEWTON_OPT), faces(), angleAppears(vpMath::rad(89)),
183 angleDisappears(vpMath::rad(89)), distNearClip(0.001), distFarClip(100), clippingFlag(vpPolygon3D::NO_CLIPPING),
184 useOgre(false), ogreShowConfigDialog(false), useScanLine(false), nbPoints(0), nbLines(0), nbPolygonLines(0),
185 nbPolygonPoints(0), nbCylinders(0), nbCircles(0), useLodGeneral(false), applyLodSettingInConfig(false),
186 minLineLengthThresholdGeneral(50.0), minPolygonAreaThresholdGeneral(2500.0), mapOfParameterNames(),
187 m_computeInteraction(true), m_lambda(1.0), m_maxIter(30), m_stopCriteriaEpsilon(1e-8), m_initialMu(0.01),
188 m_projectionErrorLines(), m_projectionErrorCylinders(), m_projectionErrorCircles(),
189 m_projectionErrorFaces(), m_projectionErrorOgreShowConfigDialog(false),
190 m_projectionErrorMe(), m_projectionErrorKernelSize(2), m_SobelX(5,5), m_SobelY(5,5),
191 m_projectionErrorDisplay(false), m_projectionErrorDisplayLength(20), m_projectionErrorDisplayThickness(1),
192 m_projectionErrorCam(), m_mask(NULL), m_I(), m_sodb_init_called(false), m_rand()
193 {
194 oJo.eye();
195 // Map used to parse additional information in CAO model files,
196 // like name of faces or LOD setting
197 mapOfParameterNames["name"] = "string";
198 mapOfParameterNames["minPolygonAreaThreshold"] = "number";
199 mapOfParameterNames["minLineLengthThreshold"] = "number";
200 mapOfParameterNames["useLod"] = "boolean";
201
202 vpImageFilter::getSobelKernelX(m_SobelX.data, m_projectionErrorKernelSize);
203 vpImageFilter::getSobelKernelY(m_SobelY.data, m_projectionErrorKernelSize);
204 }
205
~vpMbTracker()206 vpMbTracker::~vpMbTracker() {
207 for (std::vector<vpMbtDistanceLine *>::const_iterator it = m_projectionErrorLines.begin(); it != m_projectionErrorLines.end(); ++it) {
208 vpMbtDistanceLine *l = *it;
209 if (l != NULL)
210 delete l;
211 l = NULL;
212 }
213
214 for (std::vector<vpMbtDistanceCylinder *>::const_iterator it = m_projectionErrorCylinders.begin(); it != m_projectionErrorCylinders.end(); ++it) {
215 vpMbtDistanceCylinder *cy = *it;
216 if (cy != NULL)
217 delete cy;
218 cy = NULL;
219 }
220
221 for (std::vector<vpMbtDistanceCircle *>::const_iterator it = m_projectionErrorCircles.begin(); it != m_projectionErrorCircles.end(); ++it) {
222 vpMbtDistanceCircle *ci = *it;
223 if (ci != NULL)
224 delete ci;
225 ci = NULL;
226 }
227 #if defined(VISP_HAVE_COIN3D) && (COIN_MAJOR_VERSION >= 2)
228 if (m_sodb_init_called) {
229 // Cleanup memory allocated by Coin library used to load a vrml model
230 SoDB::finish();
231 }
232 #endif
233 }
234
235 #ifdef VISP_HAVE_MODULE_GUI
initClick(const vpImage<unsigned char> * const I,const vpImage<vpRGBa> * const I_color,const std::string & initFile,bool displayHelp,const vpHomogeneousMatrix & T)236 void vpMbTracker::initClick(const vpImage<unsigned char> * const I, const vpImage<vpRGBa> * const I_color,
237 const std::string &initFile, bool displayHelp, const vpHomogeneousMatrix &T)
238 {
239 vpHomogeneousMatrix last_cMo;
240 vpPoseVector init_pos;
241 vpImagePoint ip;
242 vpMouseButton::vpMouseButtonType button = vpMouseButton::button1;
243
244 std::string ext = ".init";
245 std::string str_pose = "";
246 size_t pos = initFile.rfind(ext);
247
248 // Load the last poses from files
249 std::fstream finitpos;
250 std::ifstream finit;
251 char s[FILENAME_MAX];
252 if (poseSavingFilename.empty()) {
253 if (pos != std::string::npos)
254 str_pose = initFile.substr(0, pos) + ".0.pos";
255 else
256 str_pose = initFile + ".0.pos";
257
258 finitpos.open(str_pose.c_str(), std::ios::in);
259 sprintf(s, "%s", str_pose.c_str());
260 } else {
261 finitpos.open(poseSavingFilename.c_str(), std::ios::in);
262 sprintf(s, "%s", poseSavingFilename.c_str());
263 }
264 if (finitpos.fail()) {
265 std::cout << "cannot read " << s << std::endl << "cMo set to identity" << std::endl;
266 last_cMo.eye();
267 } else {
268 for (unsigned int i = 0; i < 6; i += 1) {
269 finitpos >> init_pos[i];
270 }
271
272 finitpos.close();
273 last_cMo.buildFrom(init_pos);
274
275 std::cout << "last_cMo : " << std::endl << last_cMo << std::endl;
276
277 if (I) {
278 vpDisplay::display(*I);
279 display(*I, last_cMo, m_cam, vpColor::green, 1, true);
280 vpDisplay::displayFrame(*I, last_cMo, m_cam, 0.05, vpColor::green);
281 vpDisplay::flush(*I);
282 } else {
283 vpDisplay::display(*I_color);
284 display(*I_color, last_cMo, m_cam, vpColor::green, 1, true);
285 vpDisplay::displayFrame(*I_color, last_cMo, m_cam, 0.05, vpColor::green);
286 vpDisplay::flush(*I_color);
287 }
288
289 std::cout << "No modification : left click " << std::endl;
290 std::cout << "Modify initial pose : right click " << std::endl;
291
292 if (I) {
293 vpDisplay::displayText(*I, 15, 10, "left click to validate, right click to modify initial pose", vpColor::red);
294
295 vpDisplay::flush(*I );
296
297 while (!vpDisplay::getClick(*I, ip, button))
298 ;
299 } else {
300 vpDisplay::displayText(*I_color, 15, 10, "left click to validate, right click to modify initial pose", vpColor::red);
301
302 vpDisplay::flush(*I_color);
303
304 while (!vpDisplay::getClick(*I_color, ip, button))
305 ;
306 }
307
308 }
309
310 if (!finitpos.fail() && button == vpMouseButton::button1) {
311 m_cMo = last_cMo;
312 } else {
313 vpDisplay *d_help = NULL;
314
315 if (I) {
316 vpDisplay::display(*I);
317 vpDisplay::flush(*I);
318 }
319 else {
320 vpDisplay::display(*I_color);
321 vpDisplay::flush(*I_color);
322 }
323
324 vpPose pose;
325
326 pose.clearPoint();
327
328 // file parser
329 // number of points
330 // X Y Z
331 // X Y Z
332 if (pos != std::string::npos)
333 sprintf(s, "%s", initFile.c_str());
334 else
335 sprintf(s, "%s.init", initFile.c_str());
336
337 std::cout << "Load 3D points from: " << s << std::endl;
338 finit.open(s);
339 if (finit.fail()) {
340 std::cout << "cannot read " << s << std::endl;
341 throw vpException(vpException::ioError, "Cannot open model-based tracker init file %s", s);
342 }
343
344 #ifdef VISP_HAVE_MODULE_IO
345 // Display window creation and initialisation
346 try {
347 if (displayHelp) {
348 const std::string imgExtVec[] = {".ppm", ".pgm", ".jpg", ".jpeg", ".png"};
349 std::string dispF;
350 bool foundHelpImg = false;
351 if (pos != std::string::npos) {
352 for (size_t i = 0; i < 5 && !foundHelpImg; i++) {
353 dispF = initFile.substr(0, pos) + imgExtVec[i];
354 foundHelpImg = vpIoTools::checkFilename(dispF);
355 }
356 } else {
357 for (size_t i = 0; i < 5 && !foundHelpImg; i++) {
358 dispF = initFile + imgExtVec[i];
359 foundHelpImg = vpIoTools::checkFilename(dispF);
360 }
361 }
362
363 if (foundHelpImg) {
364 std::cout << "Load image to help initialization: " << dispF << std::endl;
365 #if defined VISP_HAVE_X11
366 d_help = new vpDisplayX;
367 #elif defined VISP_HAVE_GDI
368 d_help = new vpDisplayGDI;
369 #elif defined VISP_HAVE_OPENCV
370 d_help = new vpDisplayOpenCV;
371 #endif
372
373 vpImage<vpRGBa> Iref;
374 vpImageIo::read(Iref, dispF);
375 #if defined(VISP_HAVE_X11) || defined(VISP_HAVE_GDI) || defined(VISP_HAVE_OPENCV)
376 const int winXPos = I != NULL ? I->display->getWindowXPosition() : I_color->display->getWindowXPosition();
377 const int winYPos = I != NULL ? I->display->getWindowYPosition() : I_color->display->getWindowYPosition();
378 unsigned int width = I != NULL ? I->getWidth() : I_color->getWidth();
379 d_help->init(Iref, winXPos + (int)width + 80, winYPos,
380 "Where to initialize...");
381 vpDisplay::display(Iref);
382 vpDisplay::flush(Iref);
383 #endif
384 }
385 }
386 } catch (...) {
387 if (d_help != NULL) {
388 delete d_help;
389 d_help = NULL;
390 }
391 }
392 #else //#ifdef VISP_HAVE_MODULE_IO
393 (void)(displayHelp);
394 #endif //#ifdef VISP_HAVE_MODULE_IO
395 // skip lines starting with # as comment
396 removeComment(finit);
397
398 unsigned int n3d;
399 finit >> n3d;
400 finit.ignore(256, '\n'); // skip the rest of the line
401 std::cout << "Number of 3D points " << n3d << std::endl;
402 if (n3d > 100000) {
403 throw vpException(vpException::badValue, "In %s file, the number of 3D points exceed the max allowed", s);
404 }
405
406 std::vector<vpPoint> P(n3d);
407 for (unsigned int i = 0; i < n3d; i++) {
408 // skip lines starting with # as comment
409 removeComment(finit);
410
411 vpColVector pt_3d(4, 1.0);
412 finit >> pt_3d[0];
413 finit >> pt_3d[1];
414 finit >> pt_3d[2];
415 finit.ignore(256, '\n'); // skip the rest of the line
416
417 vpColVector pt_3d_tf = T*pt_3d;
418 std::cout << "Point " << i + 1 << " with 3D coordinates: " << pt_3d_tf[0] << " " << pt_3d_tf[1] << " " << pt_3d_tf[2] << std::endl;
419
420 P[i].setWorldCoordinates(pt_3d_tf[0], pt_3d_tf[1], pt_3d_tf[2]); // (X,Y,Z)
421 }
422
423 finit.close();
424
425 bool isWellInit = false;
426 while (!isWellInit) {
427 std::vector<vpImagePoint> mem_ip;
428 for (unsigned int i = 0; i < n3d; i++) {
429 std::ostringstream text;
430 text << "Click on point " << i + 1;
431 if (I) {
432 vpDisplay::display(*I);
433 vpDisplay::displayText(*I, 15, 10, text.str(), vpColor::red);
434 for (unsigned int k = 0; k < mem_ip.size(); k++) {
435 vpDisplay::displayCross(*I, mem_ip[k], 10, vpColor::green, 2);
436 }
437 vpDisplay::flush(*I);
438 } else {
439 vpDisplay::display(*I_color);
440 vpDisplay::displayText(*I_color, 15, 10, text.str(), vpColor::red);
441 for (unsigned int k = 0; k < mem_ip.size(); k++) {
442 vpDisplay::displayCross(*I_color, mem_ip[k], 10, vpColor::green, 2);
443 }
444 vpDisplay::flush(*I_color);
445 }
446
447 std::cout << "Click on point " << i + 1 << " ";
448 double x = 0, y = 0;
449 if (I) {
450 vpDisplay::getClick(*I, ip);
451 mem_ip.push_back(ip);
452 vpDisplay::flush(*I);
453 } else {
454 vpDisplay::getClick(*I_color, ip);
455 mem_ip.push_back(ip);
456 vpDisplay::flush(*I_color);
457 }
458 vpPixelMeterConversion::convertPoint(m_cam, ip, x, y);
459 P[i].set_x(x);
460 P[i].set_y(y);
461
462 std::cout << "with 2D coordinates: " << ip << std::endl;
463
464 pose.addPoint(P[i]); // and added to the pose computation point list
465 }
466 if (I) {
467 vpDisplay::flush(*I);
468 vpDisplay::display(*I);
469 } else {
470 vpDisplay::flush(*I_color);
471 vpDisplay::display(*I_color);
472 }
473
474 vpHomogeneousMatrix cMo1, cMo2;
475 double d1, d2;
476 d1 = d2 = std::numeric_limits<double>::max();
477 try {
478 pose.computePose(vpPose::LAGRANGE, cMo1);
479 d1 = pose.computeResidual(cMo1);
480 }
481 catch(...) {
482 // Lagrange non-planar cannot work with less than 6 points
483 }
484 try {
485 pose.computePose(vpPose::DEMENTHON, cMo2);
486 d2 = pose.computeResidual(cMo2);
487 }
488 catch(...) {
489 // Should not occur
490 }
491
492 if (d1 < d2) {
493 m_cMo = cMo1;
494 } else {
495 m_cMo = cMo2;
496 }
497 pose.computePose(vpPose::VIRTUAL_VS, m_cMo);
498
499 if (I) {
500 display(*I, m_cMo, m_cam, vpColor::green, 1, true);
501 vpDisplay::displayText(*I, 15, 10, "left click to validate, right click to re initialize object", vpColor::red);
502
503 vpDisplay::flush(*I);
504
505 button = vpMouseButton::button1;
506 while (!vpDisplay::getClick(*I, ip, button))
507 ;
508
509 if (button == vpMouseButton::button1) {
510 isWellInit = true;
511 } else {
512 pose.clearPoint();
513 vpDisplay::display(*I);
514 vpDisplay::flush(*I);
515 }
516 } else {
517 display(*I_color, m_cMo, m_cam, vpColor::green, 1, true);
518 vpDisplay::displayText(*I_color, 15, 10, "left click to validate, right click to re initialize object", vpColor::red);
519
520 vpDisplay::flush(*I_color);
521
522 button = vpMouseButton::button1;
523 while (!vpDisplay::getClick(*I_color, ip, button))
524 ;
525
526 if (button == vpMouseButton::button1) {
527 isWellInit = true;
528 } else {
529 pose.clearPoint();
530 vpDisplay::display(*I_color);
531 vpDisplay::flush(*I_color);
532 }
533 }
534 }
535 if (I)
536 vpDisplay::displayFrame(*I, m_cMo, m_cam, 0.05, vpColor::red);
537 else
538 vpDisplay::displayFrame(*I_color, m_cMo, m_cam, 0.05, vpColor::red);
539
540 // save the pose into file
541 if (poseSavingFilename.empty())
542 savePose(str_pose);
543 else
544 savePose(poseSavingFilename);
545
546 if (d_help != NULL) {
547 delete d_help;
548 d_help = NULL;
549 }
550 }
551
552 std::cout << "cMo : " << std::endl << m_cMo << std::endl;
553
554 if (I)
555 init(*I);
556 else {
557 vpImageConvert::convert(*I_color, m_I);
558 init(m_I);
559 }
560 }
561
562 /*!
563 Initialise the tracker by clicking in the image on the pixels that
564 correspond to the 3D points whose coordinates are extracted from a file. In
565 this file, comments starting with # character are allowed. Notice that 3D
566 point coordinates are expressed in meter in the object frame with their X, Y
567 and Z values.
568
569 The structure of this file is the following:
570
571 \code
572 # 3D point coordinates
573 4 # Number of points in the file (minimum is four)
574 0.01 0.01 0.01 # \
575 ... # | 3D coordinates in the object frame (X, Y, Z)
576 0.01 -0.01 -0.01 # /
577 \endcode
578
579 \param I : Input grayscale image where the user has to click.
580 \param initFile : File containing the coordinates of at least 4 3D points
581 the user has to click in the image. This file should have .init extension
582 (ie teabox.init).
583 \param displayHelp : Optionnal display of an image (.ppm, .pgm, .jpg, .jpeg, .png) that
584 should have the same generic name as the init file (ie teabox.ppm or teabox.png). This
585 image may be used to show where to click. This functionality is only
586 available if visp_io module is used.
587 \param T : optional transformation matrix to transform
588 3D points expressed in the original object frame to the desired object frame.
589
590 \exception vpException::ioError : The file specified in \e initFile doesn't
591 exist.
592 */
initClick(const vpImage<unsigned char> & I,const std::string & initFile,bool displayHelp,const vpHomogeneousMatrix & T)593 void vpMbTracker::initClick(const vpImage<unsigned char> &I, const std::string &initFile, bool displayHelp,
594 const vpHomogeneousMatrix &T)
595 {
596 initClick(&I, NULL, initFile, displayHelp, T);
597 }
598
599 /*!
600 Initialise the tracker by clicking in the image on the pixels that
601 correspond to the 3D points whose coordinates are extracted from a file. In
602 this file, comments starting with # character are allowed. Notice that 3D
603 point coordinates are expressed in meter in the object frame with their X, Y
604 and Z values.
605
606 The structure of this file is the following:
607
608 \code
609 # 3D point coordinates
610 4 # Number of points in the file (minimum is four)
611 0.01 0.01 0.01 # \
612 ... # | 3D coordinates in the object frame (X, Y, Z)
613 0.01 -0.01 -0.01 # /
614 \endcode
615
616 \param I_color : Input color image where the user has to click.
617 \param initFile : File containing the coordinates of at least 4 3D points
618 the user has to click in the image. This file should have .init extension
619 (ie teabox.init).
620 \param displayHelp : Optionnal display of an image (.ppm, .pgm, .jpg, .jpeg, .png) that
621 should have the same generic name as the init file (ie teabox.ppm or teabox.png). This
622 image may be used to show where to click. This functionality is only
623 available if visp_io module is used.
624 \param T : optional transformation matrix to transform
625 3D points expressed in the original object frame to the desired object frame.
626
627 \exception vpException::ioError : The file specified in \e initFile doesn't
628 exist.
629 */
initClick(const vpImage<vpRGBa> & I_color,const std::string & initFile,bool displayHelp,const vpHomogeneousMatrix & T)630 void vpMbTracker::initClick(const vpImage<vpRGBa> &I_color, const std::string &initFile, bool displayHelp,
631 const vpHomogeneousMatrix &T)
632 {
633 initClick(NULL, &I_color, initFile, displayHelp, T);
634 }
635
initClick(const vpImage<unsigned char> * const I,const vpImage<vpRGBa> * const I_color,const std::vector<vpPoint> & points3D_list,const std::string & displayFile)636 void vpMbTracker::initClick(const vpImage<unsigned char> * const I, const vpImage<vpRGBa> * const I_color,
637 const std::vector<vpPoint> &points3D_list, const std::string &displayFile)
638 {
639 if (I) {
640 vpDisplay::display(*I);
641 vpDisplay::flush(*I);
642 } else {
643 vpDisplay::display(*I_color);
644 vpDisplay::flush(*I_color);
645 }
646
647 vpDisplay *d_help = NULL;
648
649 vpPose pose;
650 std::vector<vpPoint> P;
651 for (unsigned int i = 0; i < points3D_list.size(); i++)
652 P.push_back(vpPoint(points3D_list[i].get_oX(), points3D_list[i].get_oY(), points3D_list[i].get_oZ()));
653
654 #ifdef VISP_HAVE_MODULE_IO
655 vpImage<vpRGBa> Iref;
656 // Display window creation and initialisation
657 if (vpIoTools::checkFilename(displayFile)) {
658 try {
659 std::cout << "Load image to help initialization: " << displayFile << std::endl;
660 #if defined VISP_HAVE_X11
661 d_help = new vpDisplayX;
662 #elif defined VISP_HAVE_GDI
663 d_help = new vpDisplayGDI;
664 #elif defined VISP_HAVE_OPENCV
665 d_help = new vpDisplayOpenCV;
666 #endif
667
668 vpImageIo::read(Iref, displayFile);
669 #if defined(VISP_HAVE_X11) || defined(VISP_HAVE_GDI) || defined(VISP_HAVE_OPENCV)
670 if (I) {
671 d_help->init(Iref, I->display->getWindowXPosition() + (int)I->getWidth() + 80, I->display->getWindowYPosition(),
672 "Where to initialize...");
673 } else {
674 d_help->init(Iref, I_color->display->getWindowXPosition() + (int)I_color->getWidth() + 80, I_color->display->getWindowYPosition(),
675 "Where to initialize...");
676 }
677 vpDisplay::display(Iref);
678 vpDisplay::flush(Iref);
679 #endif
680 } catch (...) {
681 if (d_help != NULL) {
682 delete d_help;
683 d_help = NULL;
684 }
685 }
686 }
687 #else //#ifdef VISP_HAVE_MODULE_IO
688 (void)(displayFile);
689 #endif //#ifdef VISP_HAVE_MODULE_IO
690
691 vpImagePoint ip;
692 bool isWellInit = false;
693 while (!isWellInit) {
694 for (unsigned int i = 0; i < points3D_list.size(); i++) {
695 std::cout << "Click on point " << i + 1 << std::endl;
696 double x = 0, y = 0;
697 if (I) {
698 vpDisplay::getClick(*I, ip);
699 vpDisplay::displayCross(*I, ip, 5, vpColor::green);
700 vpDisplay::flush(*I);
701 } else {
702 vpDisplay::getClick(*I_color, ip);
703 vpDisplay::displayCross(*I_color, ip, 5, vpColor::green);
704 vpDisplay::flush(*I_color);
705 }
706 vpPixelMeterConversion::convertPoint(m_cam, ip, x, y);
707 P[i].set_x(x);
708 P[i].set_y(y);
709
710 std::cout << "Click on point " << ip << std::endl;
711
712 if (I) {
713 vpDisplay::displayPoint(*I, ip, vpColor::green); // display target point
714 } else {
715 vpDisplay::displayPoint(*I_color, ip, vpColor::green); // display target point
716 }
717 pose.addPoint(P[i]); // and added to the pose computation point list
718 }
719 if (I) {
720 vpDisplay::flush(*I);
721 } else {
722 vpDisplay::flush(*I_color);
723 }
724
725 vpHomogeneousMatrix cMo1, cMo2;
726 double d1, d2;
727 d1 = d2 = std::numeric_limits<double>::max();
728 try {
729 pose.computePose(vpPose::LAGRANGE, cMo1);
730 d1 = pose.computeResidual(cMo1);
731 }
732 catch(...) {
733 // Lagrange non-planar cannot work with less than 6 points
734 }
735 try {
736 pose.computePose(vpPose::DEMENTHON, cMo2);
737 d2 = pose.computeResidual(cMo2);
738 }
739 catch(...) {
740 // Should not occur
741 }
742
743 if (d1 < d2) {
744 m_cMo = cMo1;
745 } else {
746 m_cMo = cMo2;
747 }
748 pose.computePose(vpPose::VIRTUAL_VS, m_cMo);
749
750 if (I) {
751 display(*I, m_cMo, m_cam, vpColor::green, 1, true);
752 vpDisplay::displayText(*I, 15, 10, "left click to validate, right click to re initialize object", vpColor::red);
753
754 vpDisplay::flush(*I);
755
756 vpMouseButton::vpMouseButtonType button = vpMouseButton::button1;
757 while (!vpDisplay::getClick(*I, ip, button)) {
758 };
759
760 if (button == vpMouseButton::button1) {
761 isWellInit = true;
762 } else {
763 pose.clearPoint();
764 vpDisplay::display(*I);
765 vpDisplay::flush(*I);
766 }
767 } else {
768 display(*I_color, m_cMo, m_cam, vpColor::green, 1, true);
769 vpDisplay::displayText(*I_color, 15, 10, "left click to validate, right click to re initialize object", vpColor::red);
770
771 vpDisplay::flush(*I_color);
772
773 vpMouseButton::vpMouseButtonType button = vpMouseButton::button1;
774 while (!vpDisplay::getClick(*I_color, ip, button)) {
775 };
776
777 if (button == vpMouseButton::button1) {
778 isWellInit = true;
779 } else {
780 pose.clearPoint();
781 vpDisplay::display(*I_color);
782 vpDisplay::flush(*I_color);
783 }
784 }
785 }
786
787 if (I) {
788 vpDisplay::displayFrame(*I, m_cMo, m_cam, 0.05, vpColor::red);
789 } else {
790 vpDisplay::displayFrame(*I_color, m_cMo, m_cam, 0.05, vpColor::red);
791 }
792
793 if (d_help != NULL) {
794 delete d_help;
795 d_help = NULL;
796 }
797
798 if (I)
799 init(*I);
800 else {
801 vpImageConvert::convert(*I_color, m_I);
802 init(m_I);
803 }
804 }
805
806 /*!
807 Initialise the tracker by clicking in the image on the pixels that
808 correspond to the 3D points whose coordinates are given in \e points3D_list.
809
810 \param I : Input grayscale image where the user has to click.
811 \param points3D_list : List of at least 4 3D points with coordinates
812 expressed in meters in the object frame.
813 \param displayFile : Path to the
814 image used to display the help. This image may be used to show where to
815 click. This functionality is only available if visp_io module is used.
816 */
initClick(const vpImage<unsigned char> & I,const std::vector<vpPoint> & points3D_list,const std::string & displayFile)817 void vpMbTracker::initClick(const vpImage<unsigned char> &I, const std::vector<vpPoint> &points3D_list,
818 const std::string &displayFile)
819 {
820 initClick(&I, NULL, points3D_list, displayFile);
821 }
822
823 /*!
824 Initialise the tracker by clicking in the image on the pixels that
825 correspond to the 3D points whose coordinates are given in \e points3D_list.
826
827 \param I_color : Input color image where the user has to click.
828 \param points3D_list : List of at least 4 3D points with coordinates
829 expressed in meters in the object frame.
830 \param displayFile : Path to the
831 image used to display the help. This image may be used to show where to
832 click. This functionality is only available if visp_io module is used.
833 */
initClick(const vpImage<vpRGBa> & I_color,const std::vector<vpPoint> & points3D_list,const std::string & displayFile)834 void vpMbTracker::initClick(const vpImage<vpRGBa> &I_color, const std::vector<vpPoint> &points3D_list,
835 const std::string &displayFile)
836 {
837 initClick(NULL, &I_color, points3D_list, displayFile);
838 }
839 #endif //#ifdef VISP_HAVE_MODULE_GUI
840
initFromPoints(const vpImage<unsigned char> * const I,const vpImage<vpRGBa> * const I_color,const std::string & initFile)841 void vpMbTracker::initFromPoints(const vpImage<unsigned char> * const I, const vpImage<vpRGBa> * const I_color,
842 const std::string &initFile)
843 {
844 char s[FILENAME_MAX];
845 std::fstream finit;
846
847 std::string ext = ".init";
848 size_t pos = initFile.rfind(ext);
849
850 if (pos == initFile.size() - ext.size() && pos != 0)
851 sprintf(s, "%s", initFile.c_str());
852 else
853 sprintf(s, "%s.init", initFile.c_str());
854
855 std::cout << "Load 2D/3D points from: " << s << std::endl;
856 finit.open(s, std::ios::in);
857 if (finit.fail()) {
858 std::cout << "cannot read " << s << std::endl;
859 throw vpException(vpException::ioError, "Cannot open model-based tracker init file %s", s);
860 }
861
862 //********
863 // Read 3D points coordinates
864 //********
865 char c;
866 // skip lines starting with # as comment
867 finit.get(c);
868 while (!finit.fail() && (c == '#')) {
869 finit.ignore(256, '\n');
870 finit.get(c);
871 }
872 finit.unget();
873
874 unsigned int n3d;
875 finit >> n3d;
876 finit.ignore(256, '\n'); // skip the rest of the line
877 std::cout << "Number of 3D points " << n3d << std::endl;
878 if (n3d > 100000) {
879 throw vpException(vpException::badValue, "In %s file, the number of 3D points exceed the max allowed", s);
880 }
881
882 vpPoint *P = new vpPoint[n3d];
883 for (unsigned int i = 0; i < n3d; i++) {
884 // skip lines starting with # as comment
885 finit.get(c);
886 while (!finit.fail() && (c == '#')) {
887 finit.ignore(256, '\n');
888 finit.get(c);
889 }
890 finit.unget();
891 double X, Y, Z;
892 finit >> X;
893 finit >> Y;
894 finit >> Z;
895 finit.ignore(256, '\n'); // skip the rest of the line
896
897 std::cout << "Point " << i + 1 << " with 3D coordinates: " << X << " " << Y << " " << Z << std::endl;
898 P[i].setWorldCoordinates(X, Y, Z); // (X,Y,Z)
899 }
900
901 //********
902 // Read 3D points coordinates
903 //********
904 // skip lines starting with # as comment
905 finit.get(c);
906 while (!finit.fail() && (c == '#')) {
907 finit.ignore(256, '\n');
908 finit.get(c);
909 }
910 finit.unget();
911
912 unsigned int n2d;
913 finit >> n2d;
914 finit.ignore(256, '\n'); // skip the rest of the line
915 std::cout << "Number of 2D points " << n2d << std::endl;
916 if (n2d > 100000) {
917 delete[] P;
918 throw vpException(vpException::badValue, "In %s file, the number of 2D points exceed the max allowed", s);
919 }
920
921 if (n3d != n2d) {
922 delete[] P;
923 throw vpException(vpException::badValue,
924 "In %s file, number of 2D points %d and number of 3D "
925 "points %d are not equal",
926 s, n2d, n3d);
927 }
928
929 vpPose pose;
930 for (unsigned int i = 0; i < n2d; i++) {
931 // skip lines starting with # as comment
932 finit.get(c);
933 while (!finit.fail() && (c == '#')) {
934 finit.ignore(256, '\n');
935 finit.get(c);
936 }
937 finit.unget();
938 double u, v, x = 0, y = 0;
939 finit >> v;
940 finit >> u;
941 finit.ignore(256, '\n'); // skip the rest of the line
942
943 vpImagePoint ip(v, u);
944 std::cout << "Point " << i + 1 << " with 2D coordinates: " << ip << std::endl;
945 vpPixelMeterConversion::convertPoint(m_cam, ip, x, y);
946 P[i].set_x(x);
947 P[i].set_y(y);
948 pose.addPoint(P[i]);
949 }
950
951 finit.close();
952
953 vpHomogeneousMatrix cMo1, cMo2;
954 double d1, d2;
955 d1 = d2 = std::numeric_limits<double>::max();
956 try {
957 pose.computePose(vpPose::LAGRANGE, cMo1);
958 d1 = pose.computeResidual(cMo1);
959 }
960 catch(...) {
961 // Lagrange non-planar cannot work with less than 6 points
962 }
963 try {
964 pose.computePose(vpPose::DEMENTHON, cMo2);
965 d2 = pose.computeResidual(cMo2);
966 }
967 catch(...) {
968 // Should not occur
969 }
970
971 if (d1 < d2)
972 m_cMo = cMo1;
973 else
974 m_cMo = cMo2;
975
976 pose.computePose(vpPose::VIRTUAL_VS, m_cMo);
977
978 delete[] P;
979
980 if (I) {
981 init(*I);
982 } else {
983 vpImageConvert::convert(*I_color, m_I);
984 init(m_I);
985 }
986 }
987
988 /*!
989 Initialise the tracker by reading 3D point coordinates and the corresponding
990 2D image point coordinates from a file. Comments starting with # character
991 are allowed. 3D point coordinates are expressed in meter in the object frame
992 with X, Y and Z values. 2D point coordinates are expressied in pixel
993 coordinates, with first the line and then the column of the pixel in the
994 image. The structure of this file is the following.
995 \code
996 # 3D point coordinates
997 4 # Number of 3D points in the file (minimum is four)
998 0.01 0.01 0.01 # \
999 ... # | 3D coordinates in meters in the object frame
1000 0.01 -0.01 -0.01 # /
1001 # corresponding 2D point coordinates
1002 4 # Number of image points in the file (has to be the same
1003 as the number of 3D points)
1004 100 200 # \
1005 ... # | 2D coordinates in pixel in the image
1006 50 10 # /
1007 \endcode
1008
1009 \param I : Input grayscale image
1010 \param initFile : Path to the file containing all the points.
1011 */
initFromPoints(const vpImage<unsigned char> & I,const std::string & initFile)1012 void vpMbTracker::initFromPoints(const vpImage<unsigned char> &I, const std::string &initFile)
1013 {
1014 initFromPoints(&I, NULL, initFile);
1015 }
1016
1017 /*!
1018 Initialise the tracker by reading 3D point coordinates and the corresponding
1019 2D image point coordinates from a file. Comments starting with # character
1020 are allowed. 3D point coordinates are expressed in meter in the object frame
1021 with X, Y and Z values. 2D point coordinates are expressied in pixel
1022 coordinates, with first the line and then the column of the pixel in the
1023 image. The structure of this file is the following.
1024 \code
1025 # 3D point coordinates
1026 4 # Number of 3D points in the file (minimum is four)
1027 0.01 0.01 0.01 # \
1028 ... # | 3D coordinates in meters in the object frame
1029 0.01 -0.01 -0.01 # /
1030 # corresponding 2D point coordinates
1031 4 # Number of image points in the file (has to be the same
1032 as the number of 3D points)
1033 100 200 # \
1034 ... # | 2D coordinates in pixel in the image
1035 50 10 # /
1036 \endcode
1037
1038 \param I_color : Input color image
1039 \param initFile : Path to the file containing all the points.
1040 */
initFromPoints(const vpImage<vpRGBa> & I_color,const std::string & initFile)1041 void vpMbTracker::initFromPoints(const vpImage<vpRGBa> &I_color, const std::string &initFile)
1042 {
1043 initFromPoints(NULL, &I_color, initFile);
1044 }
1045
initFromPoints(const vpImage<unsigned char> * const I,const vpImage<vpRGBa> * const I_color,const std::vector<vpImagePoint> & points2D_list,const std::vector<vpPoint> & points3D_list)1046 void vpMbTracker::initFromPoints(const vpImage<unsigned char> * const I, const vpImage<vpRGBa> * const I_color,
1047 const std::vector<vpImagePoint> &points2D_list, const std::vector<vpPoint> &points3D_list)
1048 {
1049 if (points2D_list.size() != points3D_list.size())
1050 vpERROR_TRACE("vpMbTracker::initFromPoints(), Number of 2D points "
1051 "different to the number of 3D points.");
1052
1053 size_t size = points3D_list.size();
1054 std::vector<vpPoint> P;
1055 vpPose pose;
1056
1057 for (size_t i = 0; i < size; i++) {
1058 P.push_back(vpPoint(points3D_list[i].get_oX(), points3D_list[i].get_oY(), points3D_list[i].get_oZ()));
1059 double x = 0, y = 0;
1060 vpPixelMeterConversion::convertPoint(m_cam, points2D_list[i], x, y);
1061 P[i].set_x(x);
1062 P[i].set_y(y);
1063 pose.addPoint(P[i]);
1064 }
1065
1066 vpHomogeneousMatrix cMo1, cMo2;
1067 double d1, d2;
1068 d1 = d2 = std::numeric_limits<double>::max();
1069 try {
1070 pose.computePose(vpPose::LAGRANGE, cMo1);
1071 d1 = pose.computeResidual(cMo1);
1072 }
1073 catch(...) {
1074 // Lagrange non-planar cannot work with less than 6 points
1075 }
1076 try {
1077 pose.computePose(vpPose::DEMENTHON, cMo2);
1078 d2 = pose.computeResidual(cMo2);
1079 }
1080 catch(...) {
1081 // Should not occur
1082 }
1083
1084 if (d1 < d2)
1085 m_cMo = cMo1;
1086 else
1087 m_cMo = cMo2;
1088
1089 pose.computePose(vpPose::VIRTUAL_VS, m_cMo);
1090
1091 if (I) {
1092 init(*I);
1093 } else {
1094 vpImageConvert::convert(*I_color, m_I);
1095 init(m_I);
1096 }
1097 }
1098
1099 /*!
1100 Initialise the tracking with the list of image points (points2D_list) and
1101 the list of corresponding 3D points (object frame) (points3D_list).
1102
1103 \param I : Input grayscale image
1104 \param points2D_list : List of image points.
1105 \param points3D_list : List of 3D points (object frame).
1106 */
initFromPoints(const vpImage<unsigned char> & I,const std::vector<vpImagePoint> & points2D_list,const std::vector<vpPoint> & points3D_list)1107 void vpMbTracker::initFromPoints(const vpImage<unsigned char> &I, const std::vector<vpImagePoint> &points2D_list,
1108 const std::vector<vpPoint> &points3D_list)
1109 {
1110 initFromPoints(&I, NULL, points2D_list, points3D_list);
1111 }
1112
1113 /*!
1114 Initialise the tracking with the list of image points (points2D_list) and
1115 the list of corresponding 3D points (object frame) (points3D_list).
1116
1117 \param I_color : Input color grayscale image
1118 \param points2D_list : List of image points.
1119 \param points3D_list : List of 3D points (object frame).
1120 */
initFromPoints(const vpImage<vpRGBa> & I_color,const std::vector<vpImagePoint> & points2D_list,const std::vector<vpPoint> & points3D_list)1121 void vpMbTracker::initFromPoints(const vpImage<vpRGBa> &I_color, const std::vector<vpImagePoint> &points2D_list,
1122 const std::vector<vpPoint> &points3D_list)
1123 {
1124 initFromPoints(NULL, &I_color, points2D_list, points3D_list);
1125 }
1126
initFromPose(const vpImage<unsigned char> * const I,const vpImage<vpRGBa> * const I_color,const std::string & initFile)1127 void vpMbTracker::initFromPose(const vpImage<unsigned char> * const I, const vpImage<vpRGBa> * const I_color,
1128 const std::string &initFile)
1129 {
1130 char s[FILENAME_MAX];
1131 std::fstream finit;
1132 vpPoseVector init_pos;
1133
1134 std::string ext = ".pos";
1135 size_t pos = initFile.rfind(ext);
1136
1137 if (pos == initFile.size() - ext.size() && pos != 0)
1138 sprintf(s, "%s", initFile.c_str());
1139 else
1140 sprintf(s, "%s.pos", initFile.c_str());
1141
1142 finit.open(s, std::ios::in);
1143 if (finit.fail()) {
1144 std::cout << "cannot read " << s << std::endl;
1145 throw vpException(vpException::ioError, "cannot read init file");
1146 }
1147
1148 for (unsigned int i = 0; i < 6; i += 1) {
1149 finit >> init_pos[i];
1150 }
1151
1152 m_cMo.buildFrom(init_pos);
1153
1154 if (I) {
1155 init(*I);
1156 } else {
1157 vpImageConvert::convert(*I_color, m_I);
1158 init(m_I);
1159 }
1160 }
1161
1162 /*!
1163 Initialise the tracking thanks to the pose in vpPoseVector format, and read
1164 in the file initFile. The structure of this file is (without the comments):
1165 \code
1166 // The six value of the pose vector
1167 0.0000 // \
1168 0.0000 // |
1169 1.0000 // | Exemple of value for the pose vector where Z = 1 meter
1170 0.0000 // |
1171 0.0000 // |
1172 0.0000 // /
1173 \endcode
1174
1175 Where the three firsts lines refer to the translation and the three last to
1176 the rotation in thetaU parametrisation (see vpThetaUVector).
1177 \param I : Input grayscale image
1178 \param initFile : Path to the file containing the pose.
1179 */
initFromPose(const vpImage<unsigned char> & I,const std::string & initFile)1180 void vpMbTracker::initFromPose(const vpImage<unsigned char> &I, const std::string &initFile)
1181 {
1182 initFromPose(&I, NULL, initFile);
1183 }
1184
1185 /*!
1186 Initialise the tracking thanks to the pose in vpPoseVector format, and read
1187 in the file initFile. The structure of this file is (without the comments):
1188 \code
1189 // The six value of the pose vector
1190 0.0000 // \
1191 0.0000 // |
1192 1.0000 // | Exemple of value for the pose vector where Z = 1 meter
1193 0.0000 // |
1194 0.0000 // |
1195 0.0000 // /
1196 \endcode
1197
1198 Where the three firsts lines refer to the translation and the three last to
1199 the rotation in thetaU parametrisation (see vpThetaUVector).
1200 \param I_color : Input color image
1201 \param initFile : Path to the file containing the pose.
1202 */
initFromPose(const vpImage<vpRGBa> & I_color,const std::string & initFile)1203 void vpMbTracker::initFromPose(const vpImage<vpRGBa> &I_color, const std::string &initFile)
1204 {
1205 initFromPose(NULL, &I_color, initFile);
1206 }
1207
1208 /*!
1209 Initialise the tracking thanks to the pose.
1210
1211 \param I : Input grayscale image
1212 \param cMo : Pose matrix.
1213 */
initFromPose(const vpImage<unsigned char> & I,const vpHomogeneousMatrix & cMo)1214 void vpMbTracker::initFromPose(const vpImage<unsigned char> &I, const vpHomogeneousMatrix &cMo)
1215 {
1216 m_cMo = cMo;
1217 init(I);
1218 }
1219
1220 /*!
1221 Initialise the tracking thanks to the pose.
1222
1223 \param I_color : Input color image
1224 \param cMo : Pose matrix.
1225 */
initFromPose(const vpImage<vpRGBa> & I_color,const vpHomogeneousMatrix & cMo)1226 void vpMbTracker::initFromPose(const vpImage<vpRGBa> &I_color, const vpHomogeneousMatrix &cMo)
1227 {
1228 m_cMo = cMo;
1229 vpImageConvert::convert(I_color, m_I);
1230 init(m_I);
1231 }
1232
1233 /*!
1234 Initialise the tracking thanks to the pose vector.
1235
1236 \param I : Input grayscale image
1237 \param cPo : Pose vector.
1238 */
initFromPose(const vpImage<unsigned char> & I,const vpPoseVector & cPo)1239 void vpMbTracker::initFromPose(const vpImage<unsigned char> &I, const vpPoseVector &cPo)
1240 {
1241 vpHomogeneousMatrix _cMo(cPo);
1242 initFromPose(I, _cMo);
1243 }
1244
1245 /*!
1246 Initialise the tracking thanks to the pose vector.
1247
1248 \param I_color : Input color image
1249 \param cPo : Pose vector.
1250 */
initFromPose(const vpImage<vpRGBa> & I_color,const vpPoseVector & cPo)1251 void vpMbTracker::initFromPose(const vpImage<vpRGBa> &I_color, const vpPoseVector &cPo)
1252 {
1253 vpHomogeneousMatrix _cMo(cPo);
1254 vpImageConvert::convert(I_color, m_I);
1255 initFromPose(m_I, _cMo);
1256 }
1257
1258 /*!
1259 Save the pose in the given filename
1260
1261 \param filename : Path to the file used to save the pose.
1262 */
savePose(const std::string & filename) const1263 void vpMbTracker::savePose(const std::string &filename) const
1264 {
1265 vpPoseVector init_pos;
1266 std::fstream finitpos;
1267 char s[FILENAME_MAX];
1268
1269 sprintf(s, "%s", filename.c_str());
1270 finitpos.open(s, std::ios::out);
1271
1272 init_pos.buildFrom(m_cMo);
1273 finitpos << init_pos;
1274 finitpos.close();
1275 }
1276
addPolygon(const std::vector<vpPoint> & corners,int idFace,const std::string & polygonName,bool useLod,double minPolygonAreaThreshold,double minLineLengthThreshold)1277 void vpMbTracker::addPolygon(const std::vector<vpPoint> &corners, int idFace, const std::string &polygonName,
1278 bool useLod, double minPolygonAreaThreshold,
1279 double minLineLengthThreshold)
1280 {
1281 std::vector<vpPoint> corners_without_duplicates;
1282 corners_without_duplicates.push_back(corners[0]);
1283 for (unsigned int i = 0; i < corners.size() - 1; i++) {
1284 if (std::fabs(corners[i].get_oX() - corners[i + 1].get_oX()) >
1285 std::fabs(corners[i].get_oX()) * std::numeric_limits<double>::epsilon() ||
1286 std::fabs(corners[i].get_oY() - corners[i + 1].get_oY()) >
1287 std::fabs(corners[i].get_oY()) * std::numeric_limits<double>::epsilon() ||
1288 std::fabs(corners[i].get_oZ() - corners[i + 1].get_oZ()) >
1289 std::fabs(corners[i].get_oZ()) * std::numeric_limits<double>::epsilon()) {
1290 corners_without_duplicates.push_back(corners[i + 1]);
1291 }
1292 }
1293
1294 vpMbtPolygon polygon;
1295 polygon.setNbPoint((unsigned int)corners_without_duplicates.size());
1296 polygon.setIndex((int)idFace);
1297 polygon.setName(polygonName);
1298 polygon.setLod(useLod);
1299
1300 // //if(minPolygonAreaThreshold != -1.0) {
1301 // if(std::fabs(minPolygonAreaThreshold + 1.0) >
1302 // std::fabs(minPolygonAreaThreshold)*std::numeric_limits<double>::epsilon())
1303 // {
1304 // polygon.setMinPolygonAreaThresh(minPolygonAreaThreshold);
1305 // }
1306 //
1307 // //if(minLineLengthThreshold != -1.0) {
1308 // if(std::fabs(minLineLengthThreshold + 1.0) >
1309 // std::fabs(minLineLengthThreshold)*std::numeric_limits<double>::epsilon())
1310 // {
1311 // polygon.setMinLineLengthThresh(minLineLengthThreshold);
1312 // }
1313
1314 polygon.setMinPolygonAreaThresh(minPolygonAreaThreshold);
1315 polygon.setMinLineLengthThresh(minLineLengthThreshold);
1316
1317 for (unsigned int j = 0; j < corners_without_duplicates.size(); j++) {
1318 polygon.addPoint(j, corners_without_duplicates[j]);
1319 }
1320
1321 faces.addPolygon(&polygon);
1322
1323 if (clippingFlag != vpPolygon3D::NO_CLIPPING)
1324 faces.getPolygon().back()->setClipping(clippingFlag);
1325
1326 if ((clippingFlag & vpPolygon3D::NEAR_CLIPPING) == vpPolygon3D::NEAR_CLIPPING)
1327 faces.getPolygon().back()->setNearClippingDistance(distNearClip);
1328
1329 if ((clippingFlag & vpPolygon3D::FAR_CLIPPING) == vpPolygon3D::FAR_CLIPPING)
1330 faces.getPolygon().back()->setFarClippingDistance(distFarClip);
1331 }
1332
addPolygon(const vpPoint & p1,const vpPoint & p2,const vpPoint & p3,double radius,int idFace,const std::string & polygonName,bool useLod,double minPolygonAreaThreshold)1333 void vpMbTracker::addPolygon(const vpPoint &p1, const vpPoint &p2, const vpPoint &p3, double radius,
1334 int idFace, const std::string &polygonName, bool useLod,
1335 double minPolygonAreaThreshold)
1336 {
1337 vpMbtPolygon polygon;
1338 polygon.setNbPoint(4);
1339 polygon.setName(polygonName);
1340 polygon.setLod(useLod);
1341
1342 // //if(minPolygonAreaThreshold != -1.0) {
1343 // if(std::fabs(minPolygonAreaThreshold + 1.0) >
1344 // std::fabs(minPolygonAreaThreshold)*std::numeric_limits<double>::epsilon())
1345 // {
1346 // polygon.setMinPolygonAreaThresh(minPolygonAreaThreshold);
1347 // }
1348 polygon.setMinPolygonAreaThresh(minPolygonAreaThreshold);
1349 // Non sense to set minLineLengthThreshold for circle
1350 // but used to be coherent when applying LOD settings for all polygons
1351 polygon.setMinLineLengthThresh(minLineLengthThresholdGeneral);
1352
1353 {
1354 // Create the 4 points of the circle bounding box
1355 vpPlane plane(p1, p2, p3, vpPlane::object_frame);
1356
1357 // Matrice de passage entre world et circle frame
1358 double norm_X = sqrt(vpMath::sqr(p2.get_oX() - p1.get_oX()) + vpMath::sqr(p2.get_oY() - p1.get_oY()) +
1359 vpMath::sqr(p2.get_oZ() - p1.get_oZ()));
1360 double norm_Y = sqrt(vpMath::sqr(plane.getA()) + vpMath::sqr(plane.getB()) + vpMath::sqr(plane.getC()));
1361 vpRotationMatrix wRc;
1362 vpColVector x(3), y(3), z(3);
1363 // X axis is P2-P1
1364 x[0] = (p2.get_oX() - p1.get_oX()) / norm_X;
1365 x[1] = (p2.get_oY() - p1.get_oY()) / norm_X;
1366 x[2] = (p2.get_oZ() - p1.get_oZ()) / norm_X;
1367 // Y axis is the normal of the plane
1368 y[0] = plane.getA() / norm_Y;
1369 y[1] = plane.getB() / norm_Y;
1370 y[2] = plane.getC() / norm_Y;
1371 // Z axis = X ^ Y
1372 z = vpColVector::crossProd(x, y);
1373 for (unsigned int i = 0; i < 3; i++) {
1374 wRc[i][0] = x[i];
1375 wRc[i][1] = y[i];
1376 wRc[i][2] = z[i];
1377 }
1378
1379 vpTranslationVector wtc(p1.get_oX(), p1.get_oY(), p1.get_oZ());
1380 vpHomogeneousMatrix wMc(wtc, wRc);
1381
1382 vpColVector c_p(4); // A point in the circle frame that is on the bbox
1383 c_p[0] = radius;
1384 c_p[1] = 0;
1385 c_p[2] = radius;
1386 c_p[3] = 1;
1387
1388 // Matrix to rotate a point by 90 deg around Y in the circle frame
1389 for (unsigned int i = 0; i < 4; i++) {
1390 vpColVector w_p(4); // A point in the word frame
1391 vpHomogeneousMatrix cMc_90(vpTranslationVector(), vpRotationMatrix(0, vpMath::rad(90 * i), 0));
1392 w_p = wMc * cMc_90 * c_p;
1393
1394 vpPoint w_P;
1395 w_P.setWorldCoordinates(w_p[0], w_p[1], w_p[2]);
1396
1397 polygon.addPoint(i, w_P);
1398 }
1399 }
1400
1401 polygon.setIndex(idFace);
1402 faces.addPolygon(&polygon);
1403
1404 if (clippingFlag != vpPolygon3D::NO_CLIPPING)
1405 faces.getPolygon().back()->setClipping(clippingFlag);
1406
1407 if ((clippingFlag & vpPolygon3D::NEAR_CLIPPING) == vpPolygon3D::NEAR_CLIPPING)
1408 faces.getPolygon().back()->setNearClippingDistance(distNearClip);
1409
1410 if ((clippingFlag & vpPolygon3D::FAR_CLIPPING) == vpPolygon3D::FAR_CLIPPING)
1411 faces.getPolygon().back()->setFarClippingDistance(distFarClip);
1412 }
1413
addPolygon(const vpPoint & p1,const vpPoint & p2,int idFace,const std::string & polygonName,bool useLod,double minLineLengthThreshold)1414 void vpMbTracker::addPolygon(const vpPoint &p1, const vpPoint &p2, int idFace, const std::string &polygonName,
1415 bool useLod, double minLineLengthThreshold)
1416 {
1417 // A polygon as a single line that corresponds to the revolution axis of the
1418 // cylinder
1419 vpMbtPolygon polygon;
1420 polygon.setNbPoint(2);
1421
1422 polygon.addPoint(0, p1);
1423 polygon.addPoint(1, p2);
1424
1425 polygon.setIndex(idFace);
1426 polygon.setName(polygonName);
1427 polygon.setLod(useLod);
1428
1429 // //if(minLineLengthThreshold != -1.0) {
1430 // if(std::fabs(minLineLengthThreshold + 1.0) >
1431 // std::fabs(minLineLengthThreshold)*std::numeric_limits<double>::epsilon())
1432 // {
1433 // polygon.setMinLineLengthThresh(minLineLengthThreshold);
1434 // }
1435 polygon.setMinLineLengthThresh(minLineLengthThreshold);
1436 // Non sense to set minPolygonAreaThreshold for cylinder
1437 // but used to be coherent when applying LOD settings for all polygons
1438 polygon.setMinPolygonAreaThresh(minPolygonAreaThresholdGeneral);
1439
1440 faces.addPolygon(&polygon);
1441
1442 if (clippingFlag != vpPolygon3D::NO_CLIPPING)
1443 faces.getPolygon().back()->setClipping(clippingFlag);
1444
1445 if ((clippingFlag & vpPolygon3D::NEAR_CLIPPING) == vpPolygon3D::NEAR_CLIPPING)
1446 faces.getPolygon().back()->setNearClippingDistance(distNearClip);
1447
1448 if ((clippingFlag & vpPolygon3D::FAR_CLIPPING) == vpPolygon3D::FAR_CLIPPING)
1449 faces.getPolygon().back()->setFarClippingDistance(distFarClip);
1450 }
1451
addPolygon(const std::vector<std::vector<vpPoint>> & listFaces,int idFace,const std::string & polygonName,bool useLod,double minLineLengthThreshold)1452 void vpMbTracker::addPolygon(const std::vector<std::vector<vpPoint> > &listFaces, int idFace,
1453 const std::string &polygonName, bool useLod, double minLineLengthThreshold)
1454 {
1455 int id = idFace;
1456 for (unsigned int i = 0; i < listFaces.size(); i++) {
1457 vpMbtPolygon polygon;
1458 polygon.setNbPoint((unsigned int)listFaces[i].size());
1459 for (unsigned int j = 0; j < listFaces[i].size(); j++)
1460 polygon.addPoint(j, listFaces[i][j]);
1461
1462 polygon.setIndex(id);
1463 polygon.setName(polygonName);
1464 polygon.setIsPolygonOriented(false);
1465 polygon.setLod(useLod);
1466 polygon.setMinLineLengthThresh(minLineLengthThreshold);
1467 polygon.setMinPolygonAreaThresh(minPolygonAreaThresholdGeneral);
1468
1469 faces.addPolygon(&polygon);
1470
1471 if (clippingFlag != vpPolygon3D::NO_CLIPPING)
1472 faces.getPolygon().back()->setClipping(clippingFlag);
1473
1474 if ((clippingFlag & vpPolygon3D::NEAR_CLIPPING) == vpPolygon3D::NEAR_CLIPPING)
1475 faces.getPolygon().back()->setNearClippingDistance(distNearClip);
1476
1477 if ((clippingFlag & vpPolygon3D::FAR_CLIPPING) == vpPolygon3D::FAR_CLIPPING)
1478 faces.getPolygon().back()->setFarClippingDistance(distFarClip);
1479
1480 id++;
1481 }
1482 }
1483
1484 /*!
1485 Load a 3D model from the file in parameter. This file must either be a vrml
1486 file (.wrl) or a CAO file (.cao). CAO format is described in the
1487 loadCAOModel() method.
1488
1489 \throw vpException::ioError if the file cannot be open, or if its extension
1490 is not wrl or cao.
1491
1492 \param modelFile : the file containing the the 3D model description.
1493 The extension of this file is either .wrl or .cao.
1494 \param verbose : verbose option to print additional information when loading
1495 CAO model files which include other CAO model files.
1496 \param odTo : optional transformation matrix (currently only for .cao) to transform
1497 3D points expressed in the original object frame to the desired object frame.
1498 */
loadModel(const std::string & modelFile,bool verbose,const vpHomogeneousMatrix & odTo)1499 void vpMbTracker::loadModel(const std::string &modelFile, bool verbose, const vpHomogeneousMatrix &odTo)
1500 {
1501 std::string::const_iterator it;
1502
1503 if (vpIoTools::checkFilename(modelFile)) {
1504 it = modelFile.end();
1505 if ((*(it - 1) == 'o' && *(it - 2) == 'a' && *(it - 3) == 'c' && *(it - 4) == '.') ||
1506 (*(it - 1) == 'O' && *(it - 2) == 'A' && *(it - 3) == 'C' && *(it - 4) == '.')) {
1507 std::vector<std::string> vectorOfModelFilename;
1508 int startIdFace = (int)faces.size();
1509 nbPoints = 0;
1510 nbLines = 0;
1511 nbPolygonLines = 0;
1512 nbPolygonPoints = 0;
1513 nbCylinders = 0;
1514 nbCircles = 0;
1515 loadCAOModel(modelFile, vectorOfModelFilename, startIdFace, verbose, true, odTo);
1516 } else if ((*(it - 1) == 'l' && *(it - 2) == 'r' && *(it - 3) == 'w' && *(it - 4) == '.') ||
1517 (*(it - 1) == 'L' && *(it - 2) == 'R' && *(it - 3) == 'W' && *(it - 4) == '.')) {
1518 loadVRMLModel(modelFile);
1519 } else {
1520 throw vpException(vpException::ioError, "Error: File %s doesn't contain a cao or wrl model", modelFile.c_str());
1521 }
1522 } else {
1523 throw vpException(vpException::ioError, "Error: File %s doesn't exist", modelFile.c_str());
1524 }
1525
1526 this->modelInitialised = true;
1527 this->modelFileName = modelFile;
1528 }
1529
1530 /*!
1531 Load the 3D model of the object from a vrml file. Only LineSet and FaceSet
1532 are extracted from the vrml file.
1533
1534 \warning The cylinders extracted using this method do not use the Cylinder
1535 keyword of vrml since vrml exporter such as Blender or AC3D consider a
1536 cylinder as an IndexedFaceSet. To test whether an indexedFaceSet is a
1537 cylinder or not, the name of the geometry is read. If the name begins with
1538 "cyl" then the faceset is supposed to be a cylinder. For example, the line
1539 \code
1540 geometry DEF cyl_cylinder1 IndexedFaceSet
1541 \endcode
1542 defines a cylinder named cyl_cylinder1.
1543
1544 \throw vpException::fatalError if the file cannot be open.
1545
1546 \param modelFile : The full name of the file containing the 3D model.
1547 */
loadVRMLModel(const std::string & modelFile)1548 void vpMbTracker::loadVRMLModel(const std::string &modelFile)
1549 {
1550 #ifdef VISP_HAVE_COIN3D
1551 m_sodb_init_called = true;
1552 SoDB::init(); // Call SoDB::finish() before ending the program.
1553
1554 SoInput in;
1555 SbBool ok = in.openFile(modelFile.c_str());
1556 SoVRMLGroup *sceneGraphVRML2;
1557
1558 if (!ok) {
1559 vpERROR_TRACE("can't open file to load model");
1560 throw vpException(vpException::fatalError, "can't open file to load model");
1561 }
1562
1563 if (!in.isFileVRML2()) {
1564 SoSeparator *sceneGraph = SoDB::readAll(&in);
1565 if (sceneGraph == NULL) { /*return -1;*/
1566 }
1567 sceneGraph->ref();
1568
1569 SoToVRML2Action tovrml2;
1570 tovrml2.apply(sceneGraph);
1571
1572 sceneGraphVRML2 = tovrml2.getVRML2SceneGraph();
1573 sceneGraphVRML2->ref();
1574 sceneGraph->unref();
1575 } else {
1576 sceneGraphVRML2 = SoDB::readAllVRML(&in);
1577 if (sceneGraphVRML2 == NULL) { /*return -1;*/
1578 }
1579 sceneGraphVRML2->ref();
1580 }
1581
1582 in.closeFile();
1583
1584 vpHomogeneousMatrix transform;
1585 int indexFace = (int)faces.size();
1586 extractGroup(sceneGraphVRML2, transform, indexFace);
1587
1588 sceneGraphVRML2->unref();
1589 #else
1590 vpERROR_TRACE("coin not detected with ViSP, cannot load model : %s", modelFile.c_str());
1591 throw vpException(vpException::fatalError, "coin not detected with ViSP, cannot load model");
1592 #endif
1593 }
1594
removeComment(std::ifstream & fileId)1595 void vpMbTracker::removeComment(std::ifstream &fileId)
1596 {
1597 char c;
1598
1599 fileId.get(c);
1600 while (!fileId.fail() && (c == '#')) {
1601 fileId.ignore(std::numeric_limits<std::streamsize>::max(), fileId.widen('\n'));
1602 fileId.get(c);
1603 }
1604 if (fileId.fail()) {
1605 throw(vpException(vpException::ioError, "Reached end of file"));
1606 }
1607 fileId.unget();
1608 }
1609
parseParameters(std::string & endLine)1610 std::map<std::string, std::string> vpMbTracker::parseParameters(std::string &endLine)
1611 {
1612 std::map<std::string, std::string> mapOfParams;
1613
1614 bool exit = false;
1615 while (!endLine.empty() && !exit) {
1616 exit = true;
1617
1618 for (std::map<std::string, std::string>::const_iterator it = mapOfParameterNames.begin();
1619 it != mapOfParameterNames.end(); ++it) {
1620 endLine = vpIoTools::trim(endLine);
1621 std::string param(it->first + "=");
1622
1623 // Compare with a potential parameter
1624 if (endLine.compare(0, param.size(), param) == 0) {
1625 exit = false;
1626 endLine = endLine.substr(param.size());
1627
1628 bool parseQuote = false;
1629 if (it->second == "string") {
1630 // Check if the string is between quotes
1631 if (endLine.size() > 2 && endLine[0] == '"') {
1632 parseQuote = true;
1633 endLine = endLine.substr(1);
1634 size_t pos = endLine.find_first_of('"');
1635
1636 if (pos != std::string::npos) {
1637 mapOfParams[it->first] = endLine.substr(0, pos);
1638 endLine = endLine.substr(pos + 1);
1639 } else {
1640 parseQuote = false;
1641 }
1642 }
1643 }
1644
1645 if (!parseQuote) {
1646 // Deal with space or tabulation after parameter value to substring
1647 // to the next sequence
1648 size_t pos1 = endLine.find_first_of(' ');
1649 size_t pos2 = endLine.find_first_of('\t');
1650 size_t pos = pos1 < pos2 ? pos1 : pos2;
1651
1652 mapOfParams[it->first] = endLine.substr(0, pos);
1653 endLine = endLine.substr(pos + 1);
1654 }
1655 }
1656 }
1657 }
1658
1659 return mapOfParams;
1660 }
1661
1662 /*!
1663 Load a 3D model contained in a *.cao file.
1664
1665 Since ViSP 2.9.1, lines starting with # character are considered as
1666 comments. It is also possible to add comment at the end of the lines. No
1667 specific character is requested before the comment. In the following example
1668 we use "//" but it could be an other character.
1669
1670 Since ViSP 2.9.1, circles are supported.
1671
1672 The structure of the file is :
1673 \code
1674 V1
1675 # Number of points describing the object
1676 8
1677 0.01 0.01 0.01 // point with index 0 \
1678 ... // ... | coordinates of the points in the object frame (in m.)
1679 0.01 0.01 0.01 // point with index 7 /
1680 # Number of lines to track.
1681 3
1682 0 2 // line with index 0 \
1683 1 4 // | Index of the points representing the extremitiesof the lines
1684 1 5 // line with index 2 /
1685 # Number of polygon (face) to track using the line previously described
1686 1
1687 3 0 1 2 // Face described as follow : nbLine indexLine1 indexLine2 ... indexLineN
1688 # Number of polygon (face) to track using the points previously described
1689 3
1690 4 0 2 3 4 // Face described as follow : nbPoint IndexPoint1 IndexPoint2 ... IndexPointN
1691 4 1 3 5 7 3 1 5 6
1692 # Number of cylinder
1693 1
1694 6 7 0.05 // Index of the limits points on the axis (used to know the 'height' of the cylinder) and radius of the cyclinder (in m.)
1695 # Number of circle
1696 1
1697 0.5 0 1 2 // radius, index center point, index 2 other points on the plane containing the circle
1698 \endcode
1699
1700 \param modelFile : Full name of the main *.cao file containing the model.
1701 \param vectorOfModelFilename : A vector of *.cao files.
1702 \param startIdFace : Current Id of the face.
1703 \param verbose : If true, will print additional information with CAO model
1704 files which include other CAO model files.
1705 \param parent : This parameter is
1706 set to true when parsing a parent CAO model file, and false when parsing an
1707 included CAO model file.
1708 \param odTo : optional transformation matrix (currently only for .cao) to transform
1709 3D points expressed in the original object frame to the desired object frame.
1710 */
loadCAOModel(const std::string & modelFile,std::vector<std::string> & vectorOfModelFilename,int & startIdFace,bool verbose,bool parent,const vpHomogeneousMatrix & odTo)1711 void vpMbTracker::loadCAOModel(const std::string &modelFile, std::vector<std::string> &vectorOfModelFilename,
1712 int &startIdFace, bool verbose, bool parent,
1713 const vpHomogeneousMatrix &odTo)
1714 {
1715 std::ifstream fileId;
1716 fileId.exceptions(std::ifstream::failbit | std::ifstream::eofbit);
1717 fileId.open(modelFile.c_str(), std::ifstream::in);
1718 if (fileId.fail()) {
1719 std::cout << "cannot read CAO model file: " << modelFile << std::endl;
1720 throw vpException(vpException::ioError, "cannot read CAO model file");
1721 }
1722
1723 if (verbose) {
1724 std::cout << "Model file : " << modelFile << std::endl;
1725 }
1726 vectorOfModelFilename.push_back(modelFile);
1727
1728 try {
1729 char c;
1730 // Extraction of the version (remove empty line and commented ones
1731 // (comment line begin with the #)).
1732 // while ((fileId.get(c) != NULL) && (c == '#')) fileId.ignore(256, '\n');
1733 removeComment(fileId);
1734
1735 //////////////////////////Read CAO Version (V1, V2,...)//////////////////////////
1736 int caoVersion;
1737 fileId.get(c);
1738 if (c == 'V') {
1739 fileId >> caoVersion;
1740 fileId.ignore(std::numeric_limits<std::streamsize>::max(), fileId.widen('\n')); // skip the rest of the line
1741 } else {
1742 std::cout << "in vpMbTracker::loadCAOModel() -> Bad parameter header "
1743 "file : use V0, V1, ...";
1744 throw vpException(vpException::badValue, "in vpMbTracker::loadCAOModel() -> Bad parameter "
1745 "header file : use V0, V1, ...");
1746 }
1747
1748 removeComment(fileId);
1749
1750 //////////////////////////Read the header part if present//////////////////////////
1751 std::string line;
1752 const std::string prefix_load = "load";
1753
1754 fileId.get(c);
1755 fileId.unget();
1756 bool header = false;
1757 while (c == 'l' || c == 'L') {
1758 getline(fileId, line);
1759
1760 if (!line.compare(0, prefix_load.size(), prefix_load)) {
1761 //remove "load("
1762 std::string paramsStr = line.substr(5);
1763 //get parameters inside load()
1764 paramsStr = paramsStr.substr(0, paramsStr.find_first_of(")"));
1765 //split by comma
1766 std::vector<std::string> params = vpIoTools::splitChain(paramsStr, ",");
1767 //remove whitespaces
1768 for (size_t i = 0; i < params.size(); i++) {
1769 params[i] = vpIoTools::trim(params[i]);
1770 }
1771
1772 if (!params.empty()) {
1773 // Get the loaded model pathname
1774 std::string headerPathRead = params[0];
1775 headerPathRead = headerPathRead.substr(1);
1776 headerPathRead = headerPathRead.substr(0, headerPathRead.find_first_of("\""));
1777
1778 std::string headerPath = headerPathRead;
1779 if (!vpIoTools::isAbsolutePathname(headerPathRead)) {
1780 std::string parentDirectory = vpIoTools::getParent(modelFile);
1781 headerPath = vpIoTools::createFilePath(parentDirectory, headerPathRead);
1782 }
1783
1784 // Normalize path
1785 headerPath = vpIoTools::path(headerPath);
1786
1787 // Get real path
1788 headerPath = vpIoTools::getAbsolutePathname(headerPath);
1789
1790 vpHomogeneousMatrix oTo_local;
1791 vpTranslationVector t;
1792 vpThetaUVector tu;
1793 for (size_t i = 1; i < params.size(); i++) {
1794 std::string param = params[i];
1795 {
1796 const std::string prefix = "t=[";
1797 if (!param.compare(0, prefix.size(), prefix)) {
1798 param = param.substr(prefix.size());
1799 param = param.substr(0, param.find_first_of("]"));
1800
1801 std::vector<std::string> values = vpIoTools::splitChain(param, ";");
1802 if (values.size() == 3) {
1803 t[0] = atof(values[0].c_str());
1804 t[1] = atof(values[1].c_str());
1805 t[2] = atof(values[2].c_str());
1806 }
1807 }
1808 }
1809 {
1810 const std::string prefix = "tu=[";
1811 if (!param.compare(0, prefix.size(), prefix)) {
1812 param = param.substr(prefix.size());
1813 param = param.substr(0, param.find_first_of("]"));
1814
1815 std::vector<std::string> values = vpIoTools::splitChain(param, ";");
1816 if (values.size() == 3) {
1817 for (size_t j = 0; j < values.size(); j++) {
1818 std::string value = values[j];
1819 bool radian = true;
1820 size_t unitPos = value.find("deg");
1821 if (unitPos != std::string::npos) {
1822 value = value.substr(0, unitPos);
1823 radian = false;
1824 }
1825
1826 unitPos = value.find("rad");
1827 if (unitPos != std::string::npos) {
1828 value = value.substr(0, unitPos);
1829 }
1830 tu[static_cast<unsigned int>(j)] = !radian ? vpMath::rad(atof(value.c_str())) : atof(value.c_str());
1831 }
1832 }
1833 }
1834 }
1835 }
1836 oTo_local.buildFrom(t, tu);
1837
1838 bool cyclic = false;
1839 for (std::vector<std::string>::const_iterator it = vectorOfModelFilename.begin();
1840 it != vectorOfModelFilename.end() && !cyclic; ++it) {
1841 if (headerPath == *it) {
1842 cyclic = true;
1843 }
1844 }
1845
1846 if (!cyclic) {
1847 if (vpIoTools::checkFilename(headerPath)) {
1848 header = true;
1849 loadCAOModel(headerPath, vectorOfModelFilename, startIdFace, verbose, false, odTo*oTo_local);
1850 } else {
1851 throw vpException(vpException::ioError, "file cannot be open");
1852 }
1853 } else {
1854 std::cout << "WARNING Cyclic dependency detected with file " << headerPath << " declared in " << modelFile
1855 << std::endl;
1856 }
1857 }
1858 }
1859
1860 removeComment(fileId);
1861 fileId.get(c);
1862 fileId.unget();
1863 }
1864
1865 //////////////////////////Read the point declaration part//////////////////////////
1866 unsigned int caoNbrPoint;
1867 fileId >> caoNbrPoint;
1868 fileId.ignore(std::numeric_limits<std::streamsize>::max(), fileId.widen('\n')); // skip the rest of the line
1869
1870 nbPoints += caoNbrPoint;
1871 if (verbose || (parent && !header)) {
1872 std::cout << "> " << caoNbrPoint << " points" << std::endl;
1873 }
1874
1875 if (caoNbrPoint > 100000) {
1876 throw vpException(vpException::badValue, "Exceed the max number of points in the CAO model.");
1877 }
1878
1879 if (caoNbrPoint == 0 && !header) {
1880 throw vpException(vpException::badValue, "in vpMbTracker::loadCAOModel() -> no points are defined");
1881 }
1882 vpPoint *caoPoints = new vpPoint[caoNbrPoint];
1883
1884 int i; // image coordinate (used for matching)
1885 int j;
1886
1887 for (unsigned int k = 0; k < caoNbrPoint; k++) {
1888 removeComment(fileId);
1889
1890 vpColVector pt_3d(4, 1.0);
1891 fileId >> pt_3d[0];
1892 fileId >> pt_3d[1];
1893 fileId >> pt_3d[2];
1894
1895 if (caoVersion == 2) {
1896 fileId >> i;
1897 fileId >> j;
1898 }
1899
1900 fileId.ignore(std::numeric_limits<std::streamsize>::max(), fileId.widen('\n')); // skip the rest of the line
1901
1902 vpColVector pt_3d_tf = odTo*pt_3d;
1903 caoPoints[k].setWorldCoordinates(pt_3d_tf[0], pt_3d_tf[1], pt_3d_tf[2]);
1904 }
1905
1906 removeComment(fileId);
1907
1908 //////////////////////////Read the segment declaration part//////////////////////////
1909 // Store in a map the potential segments to add
1910 std::map<std::pair<unsigned int, unsigned int>, SegmentInfo> segmentTemporaryMap;
1911 unsigned int caoNbrLine;
1912 fileId >> caoNbrLine;
1913 fileId.ignore(std::numeric_limits<std::streamsize>::max(), fileId.widen('\n')); // skip the rest of the line
1914
1915 nbLines += caoNbrLine;
1916 unsigned int *caoLinePoints = NULL;
1917 if (verbose || (parent && !header)) {
1918 std::cout << "> " << caoNbrLine << " lines" << std::endl;
1919 }
1920
1921 if (caoNbrLine > 100000) {
1922 delete[] caoPoints;
1923 throw vpException(vpException::badValue, "Exceed the max number of lines in the CAO model.");
1924 }
1925
1926 if (caoNbrLine > 0)
1927 caoLinePoints = new unsigned int[2 * caoNbrLine];
1928
1929 unsigned int index1, index2;
1930 // Initialization of idFace with startIdFace for dealing with recursive
1931 // load in header
1932 int idFace = startIdFace;
1933
1934 for (unsigned int k = 0; k < caoNbrLine; k++) {
1935 removeComment(fileId);
1936
1937 fileId >> index1;
1938 fileId >> index2;
1939
1940 //////////////////////////Read the parameter value if present//////////////////////////
1941 // Get the end of the line
1942 std::string endLine = "";
1943 if (safeGetline(fileId, endLine).good()) {
1944 std::map<std::string, std::string> mapOfParams = parseParameters(endLine);
1945
1946 std::string segmentName = "";
1947 double minLineLengthThresh = !applyLodSettingInConfig ? minLineLengthThresholdGeneral : 50.0;
1948 bool useLod = !applyLodSettingInConfig ? useLodGeneral : false;
1949 if (mapOfParams.find("name") != mapOfParams.end()) {
1950 segmentName = mapOfParams["name"];
1951 }
1952 if (mapOfParams.find("minLineLengthThreshold") != mapOfParams.end()) {
1953 minLineLengthThresh = std::atof(mapOfParams["minLineLengthThreshold"].c_str());
1954 }
1955 if (mapOfParams.find("useLod") != mapOfParams.end()) {
1956 useLod = vpIoTools::parseBoolean(mapOfParams["useLod"]);
1957 }
1958
1959 SegmentInfo segmentInfo;
1960 segmentInfo.name = segmentName;
1961 segmentInfo.useLod = useLod;
1962 segmentInfo.minLineLengthThresh = minLineLengthThresh;
1963
1964 caoLinePoints[2 * k] = index1;
1965 caoLinePoints[2 * k + 1] = index2;
1966
1967 if (index1 < caoNbrPoint && index2 < caoNbrPoint) {
1968 std::vector<vpPoint> extremities;
1969 extremities.push_back(caoPoints[index1]);
1970 extremities.push_back(caoPoints[index2]);
1971 segmentInfo.extremities = extremities;
1972
1973 std::pair<unsigned int, unsigned int> key(index1, index2);
1974
1975 segmentTemporaryMap[key] = segmentInfo;
1976 } else {
1977 vpTRACE(" line %d has wrong coordinates.", k);
1978 }
1979 }
1980 }
1981
1982 removeComment(fileId);
1983
1984 //////////////////////////Read the face segment declaration part//////////////////////////
1985 /* Load polygon from the lines extracted earlier (the first point of the
1986 * line is used)*/
1987 // Store in a vector the indexes of the segments added in the face segment
1988 // case
1989 std::vector<std::pair<unsigned int, unsigned int> > faceSegmentKeyVector;
1990 unsigned int caoNbrPolygonLine;
1991 fileId >> caoNbrPolygonLine;
1992 fileId.ignore(std::numeric_limits<std::streamsize>::max(), fileId.widen('\n')); // skip the rest of the line
1993
1994 nbPolygonLines += caoNbrPolygonLine;
1995 if (verbose || (parent && !header)) {
1996 std::cout << "> " << caoNbrPolygonLine << " polygon lines" << std::endl;
1997 }
1998
1999 if (caoNbrPolygonLine > 100000) {
2000 delete[] caoPoints;
2001 delete[] caoLinePoints;
2002 throw vpException(vpException::badValue, "Exceed the max number of polygon lines.");
2003 }
2004
2005 unsigned int index;
2006 for (unsigned int k = 0; k < caoNbrPolygonLine; k++) {
2007 removeComment(fileId);
2008
2009 unsigned int nbLinePol;
2010 fileId >> nbLinePol;
2011 std::vector<vpPoint> corners;
2012 if (nbLinePol > 100000) {
2013 throw vpException(vpException::badValue, "Exceed the max number of lines.");
2014 }
2015
2016 for (unsigned int n = 0; n < nbLinePol; n++) {
2017 fileId >> index;
2018
2019 if (index >= caoNbrLine) {
2020 throw vpException(vpException::badValue, "Exceed the max number of lines.");
2021 }
2022 corners.push_back(caoPoints[caoLinePoints[2 * index]]);
2023 corners.push_back(caoPoints[caoLinePoints[2 * index + 1]]);
2024
2025 std::pair<unsigned int, unsigned int> key(caoLinePoints[2 * index], caoLinePoints[2 * index + 1]);
2026 faceSegmentKeyVector.push_back(key);
2027 }
2028
2029 //////////////////////////Read the parameter value if present//////////////////////////
2030 // Get the end of the line
2031 std::string endLine = "";
2032 if (safeGetline(fileId, endLine).good()) {
2033 std::map<std::string, std::string> mapOfParams = parseParameters(endLine);
2034
2035 std::string polygonName = "";
2036 bool useLod = !applyLodSettingInConfig ? useLodGeneral : false;
2037 double minPolygonAreaThreshold = !applyLodSettingInConfig ? minPolygonAreaThresholdGeneral : 2500.0;
2038 if (mapOfParams.find("name") != mapOfParams.end()) {
2039 polygonName = mapOfParams["name"];
2040 }
2041 if (mapOfParams.find("minPolygonAreaThreshold") != mapOfParams.end()) {
2042 minPolygonAreaThreshold = std::atof(mapOfParams["minPolygonAreaThreshold"].c_str());
2043 }
2044 if (mapOfParams.find("useLod") != mapOfParams.end()) {
2045 useLod = vpIoTools::parseBoolean(mapOfParams["useLod"]);
2046 }
2047
2048 addPolygon(corners, idFace, polygonName, useLod, minPolygonAreaThreshold, minLineLengthThresholdGeneral);
2049 initFaceFromLines(*(faces.getPolygon().back())); // Init from the last polygon that was added
2050
2051 addProjectionErrorPolygon(corners, idFace++, polygonName, useLod, minPolygonAreaThreshold, minLineLengthThresholdGeneral);
2052 initProjectionErrorFaceFromLines(*(m_projectionErrorFaces.getPolygon().back()));
2053 }
2054 }
2055
2056 // Add the segments which were not already added in the face segment case
2057 for (std::map<std::pair<unsigned int, unsigned int>, SegmentInfo>::const_iterator it = segmentTemporaryMap.begin();
2058 it != segmentTemporaryMap.end(); ++it) {
2059 if (std::find(faceSegmentKeyVector.begin(), faceSegmentKeyVector.end(), it->first) ==
2060 faceSegmentKeyVector.end()) {
2061 addPolygon(it->second.extremities, idFace, it->second.name, it->second.useLod, minPolygonAreaThresholdGeneral,
2062 it->second.minLineLengthThresh);
2063 initFaceFromCorners(*(faces.getPolygon().back())); // Init from the last polygon that was added
2064
2065 addProjectionErrorPolygon(it->second.extremities, idFace++, it->second.name, it->second.useLod, minPolygonAreaThresholdGeneral,
2066 it->second.minLineLengthThresh);
2067 initProjectionErrorFaceFromCorners(*(m_projectionErrorFaces.getPolygon().back()));
2068 }
2069 }
2070
2071 removeComment(fileId);
2072
2073 //////////////////////////Read the face point declaration part//////////////////////////
2074 /* Extract the polygon using the point coordinates (top of the file) */
2075 unsigned int caoNbrPolygonPoint;
2076 fileId >> caoNbrPolygonPoint;
2077 fileId.ignore(std::numeric_limits<std::streamsize>::max(), fileId.widen('\n')); // skip the rest of the line
2078
2079 nbPolygonPoints += caoNbrPolygonPoint;
2080 if (verbose || (parent && !header)) {
2081 std::cout << "> " << caoNbrPolygonPoint << " polygon points" << std::endl;
2082 }
2083
2084 if (caoNbrPolygonPoint > 100000) {
2085 throw vpException(vpException::badValue, "Exceed the max number of polygon point.");
2086 }
2087
2088 for (unsigned int k = 0; k < caoNbrPolygonPoint; k++) {
2089 removeComment(fileId);
2090
2091 unsigned int nbPointPol;
2092 fileId >> nbPointPol;
2093 if (nbPointPol > 100000) {
2094 throw vpException(vpException::badValue, "Exceed the max number of points.");
2095 }
2096 std::vector<vpPoint> corners;
2097 for (unsigned int n = 0; n < nbPointPol; n++) {
2098 fileId >> index;
2099 if (index > caoNbrPoint - 1) {
2100 throw vpException(vpException::badValue, "Exceed the max number of points.");
2101 }
2102 corners.push_back(caoPoints[index]);
2103 }
2104
2105 //////////////////////////Read the parameter value if present//////////////////////////
2106 // Get the end of the line
2107 std::string endLine = "";
2108 if (safeGetline(fileId, endLine).good()) {
2109 std::map<std::string, std::string> mapOfParams = parseParameters(endLine);
2110
2111 std::string polygonName = "";
2112 bool useLod = !applyLodSettingInConfig ? useLodGeneral : false;
2113 double minPolygonAreaThreshold = !applyLodSettingInConfig ? minPolygonAreaThresholdGeneral : 2500.0;
2114 if (mapOfParams.find("name") != mapOfParams.end()) {
2115 polygonName = mapOfParams["name"];
2116 }
2117 if (mapOfParams.find("minPolygonAreaThreshold") != mapOfParams.end()) {
2118 minPolygonAreaThreshold = std::atof(mapOfParams["minPolygonAreaThreshold"].c_str());
2119 }
2120 if (mapOfParams.find("useLod") != mapOfParams.end()) {
2121 useLod = vpIoTools::parseBoolean(mapOfParams["useLod"]);
2122 }
2123
2124 addPolygon(corners, idFace, polygonName, useLod, minPolygonAreaThreshold, minLineLengthThresholdGeneral);
2125 initFaceFromCorners(*(faces.getPolygon().back())); // Init from the last polygon that was added
2126
2127 addProjectionErrorPolygon(corners, idFace++, polygonName, useLod, minPolygonAreaThreshold, minLineLengthThresholdGeneral);
2128 initProjectionErrorFaceFromCorners(*(m_projectionErrorFaces.getPolygon().back()));
2129 }
2130 }
2131
2132 //////////////////////////Read the cylinder declaration part//////////////////////////
2133 unsigned int caoNbCylinder;
2134 try {
2135 removeComment(fileId);
2136
2137 if (fileId.eof()) { // check if not at the end of the file (for old
2138 // style files)
2139 delete[] caoPoints;
2140 delete[] caoLinePoints;
2141 return;
2142 }
2143
2144 /* Extract the cylinders */
2145 fileId >> caoNbCylinder;
2146 fileId.ignore(std::numeric_limits<std::streamsize>::max(), fileId.widen('\n')); // skip the rest of the line
2147
2148 nbCylinders += caoNbCylinder;
2149 if (verbose || (parent && !header)) {
2150 std::cout << "> " << caoNbCylinder << " cylinders" << std::endl;
2151 }
2152
2153 if (caoNbCylinder > 100000) {
2154 throw vpException(vpException::badValue, "Exceed the max number of cylinders.");
2155 }
2156
2157 for (unsigned int k = 0; k < caoNbCylinder; ++k) {
2158 removeComment(fileId);
2159
2160 double radius;
2161 unsigned int indexP1, indexP2;
2162 fileId >> indexP1;
2163 fileId >> indexP2;
2164 fileId >> radius;
2165
2166 //////////////////////////Read the parameter value if present//////////////////////////
2167 // Get the end of the line
2168 std::string endLine = "";
2169 if (safeGetline(fileId, endLine).good()) {
2170 std::map<std::string, std::string> mapOfParams = parseParameters(endLine);
2171
2172 std::string polygonName = "";
2173 bool useLod = !applyLodSettingInConfig ? useLodGeneral : false;
2174 double minLineLengthThreshold = !applyLodSettingInConfig ? minLineLengthThresholdGeneral : 50.0;
2175 if (mapOfParams.find("name") != mapOfParams.end()) {
2176 polygonName = mapOfParams["name"];
2177 }
2178 if (mapOfParams.find("minLineLengthThreshold") != mapOfParams.end()) {
2179 minLineLengthThreshold = std::atof(mapOfParams["minLineLengthThreshold"].c_str());
2180 }
2181 if (mapOfParams.find("useLod") != mapOfParams.end()) {
2182 useLod = vpIoTools::parseBoolean(mapOfParams["useLod"]);
2183 }
2184
2185 int idRevolutionAxis = idFace;
2186 addPolygon(caoPoints[indexP1], caoPoints[indexP2], idFace, polygonName, useLod, minLineLengthThreshold);
2187
2188 addProjectionErrorPolygon(caoPoints[indexP1], caoPoints[indexP2], idFace++, polygonName, useLod, minLineLengthThreshold);
2189
2190 std::vector<std::vector<vpPoint> > listFaces;
2191 createCylinderBBox(caoPoints[indexP1], caoPoints[indexP2], radius, listFaces);
2192 addPolygon(listFaces, idFace, polygonName, useLod, minLineLengthThreshold);
2193
2194 initCylinder(caoPoints[indexP1], caoPoints[indexP2], radius, idRevolutionAxis, polygonName);
2195
2196 addProjectionErrorPolygon(listFaces, idFace, polygonName, useLod, minLineLengthThreshold);
2197 initProjectionErrorCylinder(caoPoints[indexP1], caoPoints[indexP2], radius, idRevolutionAxis, polygonName);
2198
2199 idFace += 4;
2200 }
2201 }
2202
2203 } catch (const std::exception& e) {
2204 std::cerr << "Cannot get the number of cylinders. Defaulting to zero." << std::endl;
2205 std::cerr << "Exception: " << e.what() << std::endl;
2206 caoNbCylinder = 0;
2207 }
2208
2209 //////////////////////////Read the circle declaration part//////////////////////////
2210 unsigned int caoNbCircle;
2211 try {
2212 removeComment(fileId);
2213
2214 if (fileId.eof()) { // check if not at the end of the file (for old
2215 // style files)
2216 delete[] caoPoints;
2217 delete[] caoLinePoints;
2218 return;
2219 }
2220
2221 /* Extract the circles */
2222 fileId >> caoNbCircle;
2223 fileId.ignore(std::numeric_limits<std::streamsize>::max(), fileId.widen('\n')); // skip the rest of the line
2224
2225 nbCircles += caoNbCircle;
2226 if (verbose || (parent && !header)) {
2227 std::cout << "> " << caoNbCircle << " circles" << std::endl;
2228 }
2229
2230 if (caoNbCircle > 100000) {
2231 throw vpException(vpException::badValue, "Exceed the max number of cicles.");
2232 }
2233
2234 for (unsigned int k = 0; k < caoNbCircle; ++k) {
2235 removeComment(fileId);
2236
2237 double radius;
2238 unsigned int indexP1, indexP2, indexP3;
2239 fileId >> radius;
2240 fileId >> indexP1;
2241 fileId >> indexP2;
2242 fileId >> indexP3;
2243
2244 //////////////////////////Read the parameter value if present//////////////////////////
2245 // Get the end of the line
2246 std::string endLine = "";
2247 if (safeGetline(fileId, endLine).good()) {
2248 std::map<std::string, std::string> mapOfParams = parseParameters(endLine);
2249
2250 std::string polygonName = "";
2251 bool useLod = !applyLodSettingInConfig ? useLodGeneral : false;
2252 double minPolygonAreaThreshold = !applyLodSettingInConfig ? minPolygonAreaThresholdGeneral : 2500.0;
2253 if (mapOfParams.find("name") != mapOfParams.end()) {
2254 polygonName = mapOfParams["name"];
2255 }
2256 if (mapOfParams.find("minPolygonAreaThreshold") != mapOfParams.end()) {
2257 minPolygonAreaThreshold = std::atof(mapOfParams["minPolygonAreaThreshold"].c_str());
2258 }
2259 if (mapOfParams.find("useLod") != mapOfParams.end()) {
2260 useLod = vpIoTools::parseBoolean(mapOfParams["useLod"]);
2261 }
2262
2263 addPolygon(caoPoints[indexP1], caoPoints[indexP2], caoPoints[indexP3], radius, idFace, polygonName, useLod,
2264 minPolygonAreaThreshold);
2265
2266 initCircle(caoPoints[indexP1], caoPoints[indexP2], caoPoints[indexP3], radius, idFace, polygonName);
2267
2268 addProjectionErrorPolygon(caoPoints[indexP1], caoPoints[indexP2], caoPoints[indexP3], radius, idFace, polygonName, useLod,
2269 minPolygonAreaThreshold);
2270 initProjectionErrorCircle(caoPoints[indexP1], caoPoints[indexP2], caoPoints[indexP3], radius, idFace++, polygonName);
2271 }
2272 }
2273
2274 } catch (const std::exception& e) {
2275 std::cerr << "Cannot get the number of circles. Defaulting to zero." << std::endl;
2276 std::cerr << "Exception: " << e.what() << std::endl;
2277 caoNbCircle = 0;
2278 }
2279
2280 startIdFace = idFace;
2281
2282 delete[] caoPoints;
2283 delete[] caoLinePoints;
2284
2285 if (header && parent) {
2286 if (verbose) {
2287 std::cout << "Global information for " << vpIoTools::getName(modelFile) << " :" << std::endl;
2288 std::cout << "Total nb of points : " << nbPoints << std::endl;
2289 std::cout << "Total nb of lines : " << nbLines << std::endl;
2290 std::cout << "Total nb of polygon lines : " << nbPolygonLines << std::endl;
2291 std::cout << "Total nb of polygon points : " << nbPolygonPoints << std::endl;
2292 std::cout << "Total nb of cylinders : " << nbCylinders << std::endl;
2293 std::cout << "Total nb of circles : " << nbCircles << std::endl;
2294 } else {
2295 std::cout << "> " << nbPoints << " points" << std::endl;
2296 std::cout << "> " << nbLines << " lines" << std::endl;
2297 std::cout << "> " << nbPolygonLines << " polygon lines" << std::endl;
2298 std::cout << "> " << nbPolygonPoints << " polygon points" << std::endl;
2299 std::cout << "> " << nbCylinders << " cylinders" << std::endl;
2300 std::cout << "> " << nbCircles << " circles" << std::endl;
2301 }
2302 }
2303
2304 //Go up: remove current model
2305 vectorOfModelFilename.pop_back();
2306 } catch (const std::exception& e) {
2307 std::cerr << "Cannot read line!" << std::endl;
2308 std::cerr << "Exception: " << e.what() << std::endl;
2309 throw vpException(vpException::ioError, "cannot read line");
2310 }
2311 }
2312
2313 #ifdef VISP_HAVE_COIN3D
2314 /*!
2315 Extract a VRML object Group.
2316
2317 \param sceneGraphVRML2 : Current node (either Transform, or Group node).
2318 \param transform : Transformation matrix for this group.
2319 \param idFace : Index of the face.
2320 */
extractGroup(SoVRMLGroup * sceneGraphVRML2,vpHomogeneousMatrix & transform,int & idFace)2321 void vpMbTracker::extractGroup(SoVRMLGroup *sceneGraphVRML2, vpHomogeneousMatrix &transform, int &idFace)
2322 {
2323 vpHomogeneousMatrix transformCur;
2324 SoVRMLTransform *sceneGraphVRML2Trasnform = dynamic_cast<SoVRMLTransform *>(sceneGraphVRML2);
2325 if (sceneGraphVRML2Trasnform) {
2326 float rx, ry, rz, rw;
2327 sceneGraphVRML2Trasnform->rotation.getValue().getValue(rx, ry, rz, rw);
2328 vpRotationMatrix rotMat(vpQuaternionVector(rx, ry, rz, rw));
2329 // std::cout << "Rotation: " << rx << " " << ry << " " << rz << " " <<
2330 // rw << std::endl;
2331
2332 float tx, ty, tz;
2333 tx = sceneGraphVRML2Trasnform->translation.getValue()[0];
2334 ty = sceneGraphVRML2Trasnform->translation.getValue()[1];
2335 tz = sceneGraphVRML2Trasnform->translation.getValue()[2];
2336 vpTranslationVector transVec(tx, ty, tz);
2337 // std::cout << "Translation: " << tx << " " << ty << " " << tz <<
2338 // std::endl;
2339
2340 float sx, sy, sz;
2341 sx = sceneGraphVRML2Trasnform->scale.getValue()[0];
2342 sy = sceneGraphVRML2Trasnform->scale.getValue()[1];
2343 sz = sceneGraphVRML2Trasnform->scale.getValue()[2];
2344 // std::cout << "Scale: " << sx << " " << sy << " " << sz <<
2345 // std::endl;
2346
2347 for (unsigned int i = 0; i < 3; i++)
2348 rotMat[0][i] *= sx;
2349 for (unsigned int i = 0; i < 3; i++)
2350 rotMat[1][i] *= sy;
2351 for (unsigned int i = 0; i < 3; i++)
2352 rotMat[2][i] *= sz;
2353
2354 transformCur = vpHomogeneousMatrix(transVec, rotMat);
2355 transform = transform * transformCur;
2356 }
2357
2358 int nbShapes = sceneGraphVRML2->getNumChildren();
2359 // std::cout << sceneGraphVRML2->getTypeId().getName().getString() <<
2360 // std::endl; std::cout << "Nb object in VRML : " << nbShapes <<
2361 // std::endl;
2362
2363 SoNode *child;
2364
2365 for (int i = 0; i < nbShapes; i++) {
2366 vpHomogeneousMatrix transform_recursive(transform);
2367 child = sceneGraphVRML2->getChild(i);
2368
2369 if (child->getTypeId() == SoVRMLGroup::getClassTypeId()) {
2370 extractGroup((SoVRMLGroup *)child, transform_recursive, idFace);
2371 }
2372
2373 if (child->getTypeId() == SoVRMLTransform::getClassTypeId()) {
2374 extractGroup((SoVRMLTransform *)child, transform_recursive, idFace);
2375 }
2376
2377 if (child->getTypeId() == SoVRMLShape::getClassTypeId()) {
2378 SoChildList *child2list = child->getChildren();
2379 std::string name = child->getName().getString();
2380
2381 for (int j = 0; j < child2list->getLength(); j++) {
2382 if (((SoNode *)child2list->get(j))->getTypeId() == SoVRMLIndexedFaceSet::getClassTypeId()) {
2383 SoVRMLIndexedFaceSet *face_set;
2384 face_set = (SoVRMLIndexedFaceSet *)child2list->get(j);
2385 if (!strncmp(face_set->getName().getString(), "cyl", 3)) {
2386 extractCylinders(face_set, transform, idFace, name);
2387 } else {
2388 extractFaces(face_set, transform, idFace, name);
2389 }
2390 }
2391 if (((SoNode *)child2list->get(j))->getTypeId() == SoVRMLIndexedLineSet::getClassTypeId()) {
2392 SoVRMLIndexedLineSet *line_set;
2393 line_set = (SoVRMLIndexedLineSet *)child2list->get(j);
2394 extractLines(line_set, idFace, name);
2395 }
2396 }
2397 }
2398 }
2399 }
2400
2401 /*!
2402 Extract a face of the object to track from the VMRL model. This method calls
2403 the initFaceFromCorners() method implemented in the child class.
2404
2405 \param face_set : Pointer to the face in the vrml format.
2406 \param transform : Transformation matrix applied to the face.
2407 \param idFace : Face id.
2408 \param polygonName: Name of the polygon.
2409 */
extractFaces(SoVRMLIndexedFaceSet * face_set,vpHomogeneousMatrix & transform,int & idFace,const std::string & polygonName)2410 void vpMbTracker::extractFaces(SoVRMLIndexedFaceSet *face_set, vpHomogeneousMatrix &transform, int &idFace,
2411 const std::string &polygonName)
2412 {
2413 std::vector<vpPoint> corners;
2414
2415 // SoMFInt32 indexList = _face_set->coordIndex;
2416 // int indexListSize = indexList.getNum();
2417 int indexListSize = face_set->coordIndex.getNum();
2418
2419 vpColVector pointTransformed(4);
2420 vpPoint pt;
2421 SoVRMLCoordinate *coord;
2422
2423 for (int i = 0; i < indexListSize; i++) {
2424 if (face_set->coordIndex[i] == -1) {
2425 if (corners.size() > 1) {
2426 addPolygon(corners, idFace, polygonName);
2427 initFaceFromCorners(*(faces.getPolygon().back())); // Init from the last polygon that was added
2428
2429 addProjectionErrorPolygon(corners, idFace++, polygonName);
2430 initProjectionErrorFaceFromCorners(*(m_projectionErrorFaces.getPolygon().back()));
2431 corners.resize(0);
2432 }
2433 } else {
2434 coord = (SoVRMLCoordinate *)(face_set->coord.getValue());
2435 int index = face_set->coordIndex[i];
2436 pointTransformed[0] = coord->point[index].getValue()[0];
2437 pointTransformed[1] = coord->point[index].getValue()[1];
2438 pointTransformed[2] = coord->point[index].getValue()[2];
2439 pointTransformed[3] = 1.0;
2440
2441 pointTransformed = transform * pointTransformed;
2442
2443 pt.setWorldCoordinates(pointTransformed[0], pointTransformed[1], pointTransformed[2]);
2444 corners.push_back(pt);
2445 }
2446 }
2447 }
2448
2449 /*!
2450 Extract a cylinder to track from the VMRL model. This method calls
2451 the initCylinder() method implemented in the child class.
2452
2453 \warning This method extract cylinder described using an indexed face set
2454 not a cylinder set since software such as AC3D or blender export a cylinder
2455 using this data type. the object name is used, if it begins with "cyl" then
2456 this method is called otherwise the extractFaces() is used.
2457
2458 \param face_set : Pointer to the cylinder in the vrml format.
2459 \param transform : Transformation matrix applied to the cylinder.
2460 \param idFace : Id of the face.
2461 \param polygonName: Name of the polygon.
2462 */
extractCylinders(SoVRMLIndexedFaceSet * face_set,vpHomogeneousMatrix & transform,int & idFace,const std::string & polygonName)2463 void vpMbTracker::extractCylinders(SoVRMLIndexedFaceSet *face_set, vpHomogeneousMatrix &transform, int &idFace,
2464 const std::string &polygonName)
2465 {
2466 std::vector<vpPoint> corners_c1, corners_c2; // points belonging to the
2467 // first circle and to the
2468 // second one.
2469 SoVRMLCoordinate *coords = (SoVRMLCoordinate *)face_set->coord.getValue();
2470
2471 unsigned int indexListSize = (unsigned int)coords->point.getNum();
2472
2473 if (indexListSize % 2 == 1) {
2474 std::cout << "Not an even number of points when extracting a cylinder." << std::endl;
2475 throw vpException(vpException::dimensionError, "Not an even number of points when extracting a cylinder.");
2476 }
2477 corners_c1.resize(indexListSize / 2);
2478 corners_c2.resize(indexListSize / 2);
2479 vpColVector pointTransformed(4);
2480 vpPoint pt;
2481
2482 // extract all points and fill the two sets.
2483
2484 for (int i = 0; i < coords->point.getNum(); ++i) {
2485 pointTransformed[0] = coords->point[i].getValue()[0];
2486 pointTransformed[1] = coords->point[i].getValue()[1];
2487 pointTransformed[2] = coords->point[i].getValue()[2];
2488 pointTransformed[3] = 1.0;
2489
2490 pointTransformed = transform * pointTransformed;
2491
2492 pt.setWorldCoordinates(pointTransformed[0], pointTransformed[1], pointTransformed[2]);
2493
2494 if (i < (int)corners_c1.size()) {
2495 corners_c1[(unsigned int)i] = pt;
2496 } else {
2497 corners_c2[(unsigned int)i - corners_c1.size()] = pt;
2498 }
2499 }
2500
2501 vpPoint p1 = getGravityCenter(corners_c1);
2502 vpPoint p2 = getGravityCenter(corners_c2);
2503
2504 vpColVector dist(3);
2505 dist[0] = p1.get_oX() - corners_c1[0].get_oX();
2506 dist[1] = p1.get_oY() - corners_c1[0].get_oY();
2507 dist[2] = p1.get_oZ() - corners_c1[0].get_oZ();
2508 double radius_c1 = sqrt(dist.sumSquare());
2509 dist[0] = p2.get_oX() - corners_c2[0].get_oX();
2510 dist[1] = p2.get_oY() - corners_c2[0].get_oY();
2511 dist[2] = p2.get_oZ() - corners_c2[0].get_oZ();
2512 double radius_c2 = sqrt(dist.sumSquare());
2513
2514 if (std::fabs(radius_c1 - radius_c2) >
2515 (std::numeric_limits<double>::epsilon() * vpMath::maximum(radius_c1, radius_c2))) {
2516 std::cout << "Radius from the two circles of the cylinders are different." << std::endl;
2517 throw vpException(vpException::badValue, "Radius from the two circles of the cylinders are different.");
2518 }
2519
2520 // addPolygon(p1, p2, idFace, polygonName);
2521 // initCylinder(p1, p2, radius_c1, idFace++);
2522
2523 int idRevolutionAxis = idFace;
2524 addPolygon(p1, p2, idFace, polygonName);
2525
2526 addProjectionErrorPolygon(p1, p2, idFace++, polygonName);
2527
2528 std::vector<std::vector<vpPoint> > listFaces;
2529 createCylinderBBox(p1, p2, radius_c1, listFaces);
2530 addPolygon(listFaces, idFace, polygonName);
2531
2532 initCylinder(p1, p2, radius_c1, idRevolutionAxis, polygonName);
2533
2534 addProjectionErrorPolygon(listFaces, idFace, polygonName);
2535 initProjectionErrorCylinder(p1, p2, radius_c1, idRevolutionAxis, polygonName);
2536
2537 idFace += 4;
2538 }
2539
2540 /*!
2541 Extract a line of the object to track from the VMRL model. This method calls
2542 the initFaceFromCorners() method implemented in the child class.
2543
2544 \param line_set : Pointer to the line in the vrml format.
2545 \param idFace : Id of the face.
2546 \param polygonName: Name of the polygon.
2547 */
extractLines(SoVRMLIndexedLineSet * line_set,int & idFace,const std::string & polygonName)2548 void vpMbTracker::extractLines(SoVRMLIndexedLineSet *line_set, int &idFace, const std::string &polygonName)
2549 {
2550 std::vector<vpPoint> corners;
2551 corners.resize(0);
2552
2553 int indexListSize = line_set->coordIndex.getNum();
2554
2555 SbVec3f point(0, 0, 0);
2556 vpPoint pt;
2557 SoVRMLCoordinate *coord;
2558
2559 for (int i = 0; i < indexListSize; i++) {
2560 if (line_set->coordIndex[i] == -1) {
2561 if (corners.size() > 1) {
2562 addPolygon(corners, idFace, polygonName);
2563 initFaceFromCorners(*(faces.getPolygon().back())); // Init from the last polygon that was added
2564
2565 addProjectionErrorPolygon(corners, idFace++, polygonName);
2566 initProjectionErrorFaceFromCorners(*(m_projectionErrorFaces.getPolygon().back()));
2567 corners.resize(0);
2568 }
2569 } else {
2570 coord = (SoVRMLCoordinate *)(line_set->coord.getValue());
2571 int index = line_set->coordIndex[i];
2572 point[0] = coord->point[index].getValue()[0];
2573 point[1] = coord->point[index].getValue()[1];
2574 point[2] = coord->point[index].getValue()[2];
2575
2576 pt.setWorldCoordinates(point[0], point[1], point[2]);
2577 corners.push_back(pt);
2578 }
2579 }
2580 }
2581
2582 #endif // VISP_HAVE_COIN3D
2583
2584 /*!
2585 Compute the center of gravity of a set of point. This is used in the
2586 cylinder extraction to find the center of the circles.
2587
2588 \throw vpException::dimensionError if the set is empty.
2589
2590 \param pts : Set of point to extract the center of gravity.
2591 \return Center of gravity of the set.
2592 */
getGravityCenter(const std::vector<vpPoint> & pts) const2593 vpPoint vpMbTracker::getGravityCenter(const std::vector<vpPoint> &pts) const
2594 {
2595 if (pts.empty()) {
2596 std::cout << "Cannot extract center of gravity of empty set." << std::endl;
2597 throw vpException(vpException::dimensionError, "Cannot extract center of gravity of empty set.");
2598 }
2599 double oX = 0;
2600 double oY = 0;
2601 double oZ = 0;
2602 vpPoint G;
2603
2604 for (unsigned int i = 0; i < pts.size(); ++i) {
2605 oX += pts[i].get_oX();
2606 oY += pts[i].get_oY();
2607 oZ += pts[i].get_oZ();
2608 }
2609
2610 G.setWorldCoordinates(oX / pts.size(), oY / pts.size(), oZ / pts.size());
2611 return G;
2612 }
2613
2614 /*!
2615 Get the list of polygons faces (a vpPolygon representing the projection of
2616 the face in the image and a list of face corners in 3D), with the
2617 possibility to order by distance to the camera or to use the visibility
2618 check to consider if the polygon face must be retrieved or not.
2619
2620 \param orderPolygons : If true, the resulting list is ordered from the
2621 nearest polygon faces to the farther. \param useVisibility : If true, only
2622 visible faces will be retrieved. \param clipPolygon : If true, the polygons
2623 will be clipped according to the clipping flags set in vpMbTracker. \return
2624 A pair object containing the list of vpPolygon and the list of face corners.
2625 */
2626 std::pair<std::vector<vpPolygon>, std::vector<std::vector<vpPoint> > >
getPolygonFaces(bool orderPolygons,bool useVisibility,bool clipPolygon)2627 vpMbTracker::getPolygonFaces(bool orderPolygons, bool useVisibility, bool clipPolygon)
2628 {
2629 // Temporary variable to permit to order polygons by distance
2630 std::vector<vpPolygon> polygonsTmp;
2631 std::vector<std::vector<vpPoint> > roisPtTmp;
2632
2633 // Pair containing the list of vpPolygon and the list of face corners
2634 std::pair<std::vector<vpPolygon>, std::vector<std::vector<vpPoint> > > pairOfPolygonFaces;
2635
2636 for (unsigned int i = 0; i < faces.getPolygon().size(); i++) {
2637 // A face has at least 3 points
2638 if (faces.getPolygon()[i]->nbpt > 2) {
2639 if ((useVisibility && faces.getPolygon()[i]->isvisible) || !useVisibility) {
2640 std::vector<vpImagePoint> roiPts;
2641
2642 if (clipPolygon) {
2643 faces.getPolygon()[i]->getRoiClipped(m_cam, roiPts, m_cMo);
2644 } else {
2645 roiPts = faces.getPolygon()[i]->getRoi(m_cam, m_cMo);
2646 }
2647
2648 if (roiPts.size() <= 2) {
2649 continue;
2650 }
2651
2652 polygonsTmp.push_back(vpPolygon(roiPts));
2653
2654 std::vector<vpPoint> polyPts;
2655 if (clipPolygon) {
2656 faces.getPolygon()[i]->getPolygonClipped(polyPts);
2657 } else {
2658 for (unsigned int j = 0; j < faces.getPolygon()[i]->nbpt; j++) {
2659 polyPts.push_back(faces.getPolygon()[i]->p[j]);
2660 }
2661 }
2662 roisPtTmp.push_back(polyPts);
2663 }
2664 }
2665 }
2666
2667 if (orderPolygons) {
2668 // Order polygons by distance (near to far)
2669 std::vector<PolygonFaceInfo> listOfPolygonFaces;
2670 for (unsigned int i = 0; i < polygonsTmp.size(); i++) {
2671 double x_centroid = 0.0, y_centroid = 0.0, z_centroid = 0.0;
2672 for (unsigned int j = 0; j < roisPtTmp[i].size(); j++) {
2673 x_centroid += roisPtTmp[i][j].get_X();
2674 y_centroid += roisPtTmp[i][j].get_Y();
2675 z_centroid += roisPtTmp[i][j].get_Z();
2676 }
2677
2678 x_centroid /= roisPtTmp[i].size();
2679 y_centroid /= roisPtTmp[i].size();
2680 z_centroid /= roisPtTmp[i].size();
2681
2682 double squared_dist = x_centroid * x_centroid + y_centroid * y_centroid + z_centroid * z_centroid;
2683 listOfPolygonFaces.push_back(PolygonFaceInfo(squared_dist, polygonsTmp[i], roisPtTmp[i]));
2684 }
2685
2686 // Sort the list of polygon faces
2687 std::sort(listOfPolygonFaces.begin(), listOfPolygonFaces.end());
2688
2689 polygonsTmp.resize(listOfPolygonFaces.size());
2690 roisPtTmp.resize(listOfPolygonFaces.size());
2691
2692 size_t cpt = 0;
2693 for (std::vector<PolygonFaceInfo>::const_iterator it = listOfPolygonFaces.begin(); it != listOfPolygonFaces.end();
2694 ++it, cpt++) {
2695 polygonsTmp[cpt] = it->polygon;
2696 roisPtTmp[cpt] = it->faceCorners;
2697 }
2698
2699 pairOfPolygonFaces.first = polygonsTmp;
2700 pairOfPolygonFaces.second = roisPtTmp;
2701 } else {
2702 pairOfPolygonFaces.first = polygonsTmp;
2703 pairOfPolygonFaces.second = roisPtTmp;
2704 }
2705
2706 return pairOfPolygonFaces;
2707 }
2708
2709 /*!
2710 Use Ogre3D for visibility tests
2711
2712 \warning This function has to be called before the initialization of the
2713 tracker.
2714
2715 \param v : True to use it, False otherwise
2716 */
setOgreVisibilityTest(const bool & v)2717 void vpMbTracker::setOgreVisibilityTest(const bool &v)
2718 {
2719 useOgre = v;
2720 if (useOgre) {
2721 #ifndef VISP_HAVE_OGRE
2722 useOgre = false;
2723 std::cout << "WARNING: ViSP doesn't have Ogre3D, basic visibility test "
2724 "will be used. setOgreVisibilityTest() set to false."
2725 << std::endl;
2726 #endif
2727 }
2728 }
2729
2730 /*!
2731 Set the far distance for clipping.
2732
2733 \param dist : Far clipping value.
2734 */
setFarClippingDistance(const double & dist)2735 void vpMbTracker::setFarClippingDistance(const double &dist)
2736 {
2737 if ((clippingFlag & vpPolygon3D::NEAR_CLIPPING) == vpPolygon3D::NEAR_CLIPPING && dist <= distNearClip)
2738 vpTRACE("Far clipping value cannot be inferior than near clipping value. "
2739 "Far clipping won't be considered.");
2740 else if (dist < 0)
2741 vpTRACE("Far clipping value cannot be inferior than 0. Far clipping "
2742 "won't be considered.");
2743 else {
2744 clippingFlag = (clippingFlag | vpPolygon3D::FAR_CLIPPING);
2745 distFarClip = dist;
2746 for (unsigned int i = 0; i < faces.size(); i++) {
2747 faces[i]->setFarClippingDistance(distFarClip);
2748 }
2749 #ifdef VISP_HAVE_OGRE
2750 faces.getOgreContext()->setFarClippingDistance(distFarClip);
2751 #endif
2752 }
2753 }
2754
2755 /*!
2756 Set the flag to consider if the level of detail (LOD) is used.
2757
2758 \param useLod : true if the level of detail must be used, false otherwise.
2759 When true, two parameters can be set, see setMinLineLengthThresh() and
2760 setMinPolygonAreaThresh(). \param name : name of the face we want to modify
2761 the LOD parameter.
2762
2763 \sa setMinLineLengthThresh(), setMinPolygonAreaThresh()
2764 */
setLod(bool useLod,const std::string & name)2765 void vpMbTracker::setLod(bool useLod, const std::string &name)
2766 {
2767 for (unsigned int i = 0; i < faces.size(); i++) {
2768 if (name.empty() || faces[i]->name == name) {
2769 faces[i]->setLod(useLod);
2770 }
2771 }
2772 }
2773
2774 /*!
2775 Set the threshold for the minimum line length to be considered as visible
2776 in the LOD case.
2777
2778 \param minLineLengthThresh : threshold for the minimum line length in
2779 pixel. \param name : name of the face we want to modify the LOD threshold.
2780
2781 \sa setLod(), setMinPolygonAreaThresh()
2782 */
setMinLineLengthThresh(double minLineLengthThresh,const std::string & name)2783 void vpMbTracker::setMinLineLengthThresh(double minLineLengthThresh, const std::string &name)
2784 {
2785 for (unsigned int i = 0; i < faces.size(); i++) {
2786 if (name.empty() || faces[i]->name == name) {
2787 faces[i]->setMinLineLengthThresh(minLineLengthThresh);
2788 }
2789 }
2790 }
2791
2792 /*!
2793 Set the minimum polygon area to be considered as visible in the LOD case.
2794
2795 \param minPolygonAreaThresh : threshold for the minimum polygon area in
2796 pixel. \param name : name of the face we want to modify the LOD threshold.
2797
2798 \sa setLod(), setMinLineLengthThresh()
2799 */
setMinPolygonAreaThresh(double minPolygonAreaThresh,const std::string & name)2800 void vpMbTracker::setMinPolygonAreaThresh(double minPolygonAreaThresh, const std::string &name)
2801 {
2802 for (unsigned int i = 0; i < faces.size(); i++) {
2803 if (name.empty() || faces[i]->name == name) {
2804 faces[i]->setMinPolygonAreaThresh(minPolygonAreaThresh);
2805 }
2806 }
2807 }
2808
2809 /*!
2810 Set the near distance for clipping.
2811
2812 \param dist : Near clipping value.
2813 */
setNearClippingDistance(const double & dist)2814 void vpMbTracker::setNearClippingDistance(const double &dist)
2815 {
2816 if ((clippingFlag & vpPolygon3D::FAR_CLIPPING) == vpPolygon3D::FAR_CLIPPING && dist >= distFarClip)
2817 vpTRACE("Near clipping value cannot be superior than far clipping value. "
2818 "Near clipping won't be considered.");
2819 else if (dist < 0)
2820 vpTRACE("Near clipping value cannot be inferior than 0. Near clipping "
2821 "won't be considered.");
2822 else {
2823 clippingFlag = (clippingFlag | vpPolygon3D::NEAR_CLIPPING);
2824 distNearClip = dist;
2825 for (unsigned int i = 0; i < faces.size(); i++) {
2826 faces[i]->setNearClippingDistance(distNearClip);
2827 }
2828 #ifdef VISP_HAVE_OGRE
2829 faces.getOgreContext()->setNearClippingDistance(distNearClip);
2830 #endif
2831 }
2832 }
2833
2834 /*!
2835 Specify which clipping to use.
2836
2837 \sa vpMbtPolygonClipping
2838
2839 \param flags : New clipping flags.
2840 */
setClipping(const unsigned int & flags)2841 void vpMbTracker::setClipping(const unsigned int &flags)
2842 {
2843 clippingFlag = flags;
2844 for (unsigned int i = 0; i < faces.size(); i++)
2845 faces[i]->setClipping(clippingFlag);
2846 }
2847
computeCovarianceMatrixVVS(const bool isoJoIdentity_,const vpColVector & w_true,const vpHomogeneousMatrix & cMoPrev,const vpMatrix & L_true,const vpMatrix & LVJ_true,const vpColVector & error)2848 void vpMbTracker::computeCovarianceMatrixVVS(const bool isoJoIdentity_, const vpColVector &w_true,
2849 const vpHomogeneousMatrix &cMoPrev, const vpMatrix &L_true,
2850 const vpMatrix &LVJ_true, const vpColVector &error)
2851 {
2852 if (computeCovariance) {
2853 vpMatrix D;
2854 D.diag(w_true);
2855
2856 // Note that here the covariance is computed on cMoPrev for time
2857 // computation efficiency
2858 if (isoJoIdentity_) {
2859 covarianceMatrix = vpMatrix::computeCovarianceMatrixVVS(cMoPrev, error, L_true, D);
2860 } else {
2861 covarianceMatrix = vpMatrix::computeCovarianceMatrixVVS(cMoPrev, error, LVJ_true, D);
2862 }
2863 }
2864 }
2865
2866 /*!
2867 Compute \f$ J^T R \f$, with J the interaction matrix and R the vector of
2868 residu.
2869
2870 \throw vpMatrixException::incorrectMatrixSizeError if the sizes of the
2871 matrices do not allow the computation.
2872
2873 \warning The JTR vector is resized.
2874
2875 \param interaction : The interaction matrix (size Nx6).
2876 \param error : The residu vector (size Nx1).
2877 \param JTR : The resulting JTR column vector (size 6x1).
2878 */
computeJTR(const vpMatrix & interaction,const vpColVector & error,vpColVector & JTR) const2879 void vpMbTracker::computeJTR(const vpMatrix &interaction, const vpColVector &error, vpColVector &JTR) const
2880 {
2881 if (interaction.getRows() != error.getRows() || interaction.getCols() != 6) {
2882 throw vpMatrixException(vpMatrixException::incorrectMatrixSizeError, "Incorrect matrices size in computeJTR.");
2883 }
2884
2885 JTR.resize(6, false);
2886
2887 SimdComputeJtR(interaction.data, interaction.getRows(), error.data, JTR.data);
2888 }
2889
computeVVSCheckLevenbergMarquardt(unsigned int iter,vpColVector & error,const vpColVector & m_error_prev,const vpHomogeneousMatrix & cMoPrev,double & mu,bool & reStartFromLastIncrement,vpColVector * const w,const vpColVector * const m_w_prev)2890 void vpMbTracker::computeVVSCheckLevenbergMarquardt(unsigned int iter, vpColVector &error,
2891 const vpColVector &m_error_prev, const vpHomogeneousMatrix &cMoPrev,
2892 double &mu, bool &reStartFromLastIncrement, vpColVector *const w,
2893 const vpColVector *const m_w_prev)
2894 {
2895 if (iter != 0 && m_optimizationMethod == vpMbTracker::LEVENBERG_MARQUARDT_OPT) {
2896 if (error.sumSquare() / (double)error.getRows() > m_error_prev.sumSquare() / (double)m_error_prev.getRows()) {
2897 mu *= 10.0;
2898
2899 if (mu > 1.0)
2900 throw vpTrackingException(vpTrackingException::fatalError, "Optimization diverged");
2901
2902 m_cMo = cMoPrev;
2903 error = m_error_prev;
2904 if (w != NULL && m_w_prev != NULL) {
2905 *w = *m_w_prev;
2906 }
2907 reStartFromLastIncrement = true;
2908 }
2909 }
2910 }
2911
computeVVSPoseEstimation(const bool isoJoIdentity_,unsigned int iter,vpMatrix & L,vpMatrix & LTL,vpColVector & R,const vpColVector & error,vpColVector & error_prev,vpColVector & LTR,double & mu,vpColVector & v,const vpColVector * const w,vpColVector * const m_w_prev)2912 void vpMbTracker::computeVVSPoseEstimation(const bool isoJoIdentity_, unsigned int iter, vpMatrix &L,
2913 vpMatrix <L, vpColVector &R, const vpColVector &error,
2914 vpColVector &error_prev, vpColVector <R, double &mu, vpColVector &v,
2915 const vpColVector *const w, vpColVector *const m_w_prev)
2916 {
2917 if (isoJoIdentity_) {
2918 LTL = L.AtA();
2919 computeJTR(L, R, LTR);
2920
2921 switch (m_optimizationMethod) {
2922 case vpMbTracker::LEVENBERG_MARQUARDT_OPT: {
2923 vpMatrix LMA(LTL.getRows(), LTL.getCols());
2924 LMA.eye();
2925 vpMatrix LTLmuI = LTL + (LMA * mu);
2926 v = -m_lambda * LTLmuI.pseudoInverse(LTLmuI.getRows() * std::numeric_limits<double>::epsilon()) * LTR;
2927
2928 if (iter != 0)
2929 mu /= 10.0;
2930
2931 error_prev = error;
2932 if (w != NULL && m_w_prev != NULL)
2933 *m_w_prev = *w;
2934 break;
2935 }
2936
2937 case vpMbTracker::GAUSS_NEWTON_OPT:
2938 default:
2939 v = -m_lambda * LTL.pseudoInverse(LTL.getRows() * std::numeric_limits<double>::epsilon()) * LTR;
2940 break;
2941 }
2942 } else {
2943 vpVelocityTwistMatrix cVo;
2944 cVo.buildFrom(m_cMo);
2945 vpMatrix LVJ = (L * (cVo * oJo));
2946 vpMatrix LVJTLVJ = (LVJ).AtA();
2947 vpColVector LVJTR;
2948 computeJTR(LVJ, R, LVJTR);
2949
2950 switch (m_optimizationMethod) {
2951 case vpMbTracker::LEVENBERG_MARQUARDT_OPT: {
2952 vpMatrix LMA(LVJTLVJ.getRows(), LVJTLVJ.getCols());
2953 LMA.eye();
2954 vpMatrix LTLmuI = LVJTLVJ + (LMA * mu);
2955 v = -m_lambda * LTLmuI.pseudoInverse(LTLmuI.getRows() * std::numeric_limits<double>::epsilon()) * LVJTR;
2956 v = cVo * v;
2957
2958 if (iter != 0)
2959 mu /= 10.0;
2960
2961 error_prev = error;
2962 if (w != NULL && m_w_prev != NULL)
2963 *m_w_prev = *w;
2964 break;
2965 }
2966 case vpMbTracker::GAUSS_NEWTON_OPT:
2967 default:
2968 v = -m_lambda * LVJTLVJ.pseudoInverse(LVJTLVJ.getRows() * std::numeric_limits<double>::epsilon()) * LVJTR;
2969 v = cVo * v;
2970 break;
2971 }
2972 }
2973 }
2974
computeVVSWeights(vpRobust & robust,const vpColVector & error,vpColVector & w)2975 void vpMbTracker::computeVVSWeights(vpRobust &robust, const vpColVector &error, vpColVector &w)
2976 {
2977 if (error.getRows() > 0)
2978 robust.MEstimator(vpRobust::TUKEY, error, w);
2979 }
2980
2981 /*!
2982 Get a 1x6 vpColVector representing the estimated degrees of freedom.
2983 vpColVector[0] = 1 if translation on X is estimated, 0 otherwise;
2984 vpColVector[1] = 1 if translation on Y is estimated, 0 otherwise;
2985 vpColVector[2] = 1 if translation on Z is estimated, 0 otherwise;
2986 vpColVector[3] = 1 if rotation on X is estimated, 0 otherwise;
2987 vpColVector[4] = 1 if rotation on Y is estimated, 0 otherwise;
2988 vpColVector[5] = 1 if rotation on Z is estimated, 0 otherwise;
2989
2990 \return 1x6 vpColVector representing the estimated degrees of freedom.
2991 */
getEstimatedDoF() const2992 vpColVector vpMbTracker::getEstimatedDoF() const
2993 {
2994 vpColVector v(6);
2995 for (unsigned int i = 0; i < 6; i++)
2996 v[i] = oJo[i][i];
2997 return v;
2998 }
2999
3000 /*!
3001 Set a 6-dim column vector representing the degrees of freedom in the object
3002 frame that are estimated by the tracker. When set to 1, all the 6 dof are
3003 estimated.
3004
3005 Below we give the correspondance between the index of the vector and the
3006 considered dof:
3007 - v[0] = 1 if translation along X is estimated, 0 otherwise;
3008 - v[1] = 1 if translation along Y is estimated, 0 otherwise;
3009 - v[2] = 1 if translation along Z is estimated, 0 otherwise;
3010 - v[3] = 1 if rotation along X is estimated, 0 otherwise;
3011 - v[4] = 1 if rotation along Y is estimated, 0 otherwise;
3012 - v[5] = 1 if rotation along Z is estimated, 0 otherwise;
3013
3014 */
setEstimatedDoF(const vpColVector & v)3015 void vpMbTracker::setEstimatedDoF(const vpColVector &v)
3016 {
3017 if (v.getRows() == 6) {
3018 isoJoIdentity = true;
3019 for (unsigned int i = 0; i < 6; i++) {
3020 // if(v[i] != 0){
3021 if (std::fabs(v[i]) > std::numeric_limits<double>::epsilon()) {
3022 oJo[i][i] = 1.0;
3023 } else {
3024 oJo[i][i] = 0.0;
3025 isoJoIdentity = false;
3026 }
3027 }
3028 }
3029 }
3030
createCylinderBBox(const vpPoint & p1,const vpPoint & p2,const double & radius,std::vector<std::vector<vpPoint>> & listFaces)3031 void vpMbTracker::createCylinderBBox(const vpPoint &p1, const vpPoint &p2, const double &radius,
3032 std::vector<std::vector<vpPoint> > &listFaces)
3033 {
3034 listFaces.clear();
3035
3036 // std::vector<vpPoint> revolutionAxis;
3037 // revolutionAxis.push_back(p1);
3038 // revolutionAxis.push_back(p2);
3039 // listFaces.push_back(revolutionAxis);
3040
3041 vpColVector axis(3);
3042 axis[0] = p1.get_oX() - p2.get_oX();
3043 axis[1] = p1.get_oY() - p2.get_oY();
3044 axis[2] = p1.get_oZ() - p2.get_oZ();
3045
3046 vpColVector randomVec(3);
3047 randomVec = 0;
3048
3049 vpColVector axisOrtho(3);
3050
3051 randomVec[0] = 1.0;
3052 axisOrtho = vpColVector::crossProd(axis, randomVec);
3053
3054 if (axisOrtho.frobeniusNorm() < std::numeric_limits<double>::epsilon()) {
3055 randomVec = 0;
3056 randomVec[1] = 1.0;
3057 axisOrtho = vpColVector::crossProd(axis, randomVec);
3058 if (axisOrtho.frobeniusNorm() < std::numeric_limits<double>::epsilon()) {
3059 randomVec = 0;
3060 randomVec[2] = 1.0;
3061 axisOrtho = vpColVector::crossProd(axis, randomVec);
3062 if (axisOrtho.frobeniusNorm() < std::numeric_limits<double>::epsilon())
3063 throw vpMatrixException(vpMatrixException::badValue, "Problem in the cylinder definition");
3064 }
3065 }
3066
3067 axisOrtho.normalize();
3068
3069 vpColVector axisOrthoBis(3);
3070 axisOrthoBis = vpColVector::crossProd(axis, axisOrtho);
3071 axisOrthoBis.normalize();
3072
3073 // First circle
3074 vpColVector p1Vec(3);
3075 p1Vec[0] = p1.get_oX();
3076 p1Vec[1] = p1.get_oY();
3077 p1Vec[2] = p1.get_oZ();
3078 vpColVector fc1 = p1Vec + axisOrtho * radius;
3079 vpColVector fc2 = p1Vec + axisOrthoBis * radius;
3080 vpColVector fc3 = p1Vec - axisOrtho * radius;
3081 vpColVector fc4 = p1Vec - axisOrthoBis * radius;
3082
3083 vpColVector p2Vec(3);
3084 p2Vec[0] = p2.get_oX();
3085 p2Vec[1] = p2.get_oY();
3086 p2Vec[2] = p2.get_oZ();
3087 vpColVector sc1 = p2Vec + axisOrtho * radius;
3088 vpColVector sc2 = p2Vec + axisOrthoBis * radius;
3089 vpColVector sc3 = p2Vec - axisOrtho * radius;
3090 vpColVector sc4 = p2Vec - axisOrthoBis * radius;
3091
3092 std::vector<vpPoint> pointsFace;
3093 pointsFace.push_back(vpPoint(fc1[0], fc1[1], fc1[2]));
3094 pointsFace.push_back(vpPoint(sc1[0], sc1[1], sc1[2]));
3095 pointsFace.push_back(vpPoint(sc2[0], sc2[1], sc2[2]));
3096 pointsFace.push_back(vpPoint(fc2[0], fc2[1], fc2[2]));
3097 listFaces.push_back(pointsFace);
3098
3099 pointsFace.clear();
3100 pointsFace.push_back(vpPoint(fc2[0], fc2[1], fc2[2]));
3101 pointsFace.push_back(vpPoint(sc2[0], sc2[1], sc2[2]));
3102 pointsFace.push_back(vpPoint(sc3[0], sc3[1], sc3[2]));
3103 pointsFace.push_back(vpPoint(fc3[0], fc3[1], fc3[2]));
3104 listFaces.push_back(pointsFace);
3105
3106 pointsFace.clear();
3107 pointsFace.push_back(vpPoint(fc3[0], fc3[1], fc3[2]));
3108 pointsFace.push_back(vpPoint(sc3[0], sc3[1], sc3[2]));
3109 pointsFace.push_back(vpPoint(sc4[0], sc4[1], sc4[2]));
3110 pointsFace.push_back(vpPoint(fc4[0], fc4[1], fc4[2]));
3111 listFaces.push_back(pointsFace);
3112
3113 pointsFace.clear();
3114 pointsFace.push_back(vpPoint(fc4[0], fc4[1], fc4[2]));
3115 pointsFace.push_back(vpPoint(sc4[0], sc4[1], sc4[2]));
3116 pointsFace.push_back(vpPoint(sc1[0], sc1[1], sc1[2]));
3117 pointsFace.push_back(vpPoint(fc1[0], fc1[1], fc1[2]));
3118 listFaces.push_back(pointsFace);
3119 }
3120
3121 /*!
3122 Check if two vpPoints are similar.
3123
3124 To be similar : \f$ (X_1 - X_2)^2 + (Y_1 - Y_2)^2 + (Z_1 - Z_2)^2 < epsilon
3125 \f$.
3126
3127 \param P1 : The first point to compare
3128 \param P2 : The second point to compare
3129 */
samePoint(const vpPoint & P1,const vpPoint & P2) const3130 bool vpMbTracker::samePoint(const vpPoint &P1, const vpPoint &P2) const
3131 {
3132 double dx = fabs(P1.get_oX() - P2.get_oX());
3133 double dy = fabs(P1.get_oY() - P2.get_oY());
3134 double dz = fabs(P1.get_oZ() - P2.get_oZ());
3135
3136 if (dx <= std::numeric_limits<double>::epsilon() && dy <= std::numeric_limits<double>::epsilon() &&
3137 dz <= std::numeric_limits<double>::epsilon())
3138 return true;
3139 else
3140 return false;
3141 }
3142
addProjectionErrorPolygon(const std::vector<vpPoint> & corners,int idFace,const std::string & polygonName,bool useLod,double minPolygonAreaThreshold,double minLineLengthThreshold)3143 void vpMbTracker::addProjectionErrorPolygon(const std::vector<vpPoint> &corners, int idFace, const std::string &polygonName,
3144 bool useLod, double minPolygonAreaThreshold,
3145 double minLineLengthThreshold)
3146 {
3147 std::vector<vpPoint> corners_without_duplicates;
3148 corners_without_duplicates.push_back(corners[0]);
3149 for (unsigned int i = 0; i < corners.size() - 1; i++) {
3150 if (std::fabs(corners[i].get_oX() - corners[i + 1].get_oX()) >
3151 std::fabs(corners[i].get_oX()) * std::numeric_limits<double>::epsilon() ||
3152 std::fabs(corners[i].get_oY() - corners[i + 1].get_oY()) >
3153 std::fabs(corners[i].get_oY()) * std::numeric_limits<double>::epsilon() ||
3154 std::fabs(corners[i].get_oZ() - corners[i + 1].get_oZ()) >
3155 std::fabs(corners[i].get_oZ()) * std::numeric_limits<double>::epsilon()) {
3156 corners_without_duplicates.push_back(corners[i + 1]);
3157 }
3158 }
3159
3160 vpMbtPolygon polygon;
3161 polygon.setNbPoint((unsigned int)corners_without_duplicates.size());
3162 polygon.setIndex((int)idFace);
3163 polygon.setName(polygonName);
3164 polygon.setLod(useLod);
3165
3166 polygon.setMinPolygonAreaThresh(minPolygonAreaThreshold);
3167 polygon.setMinLineLengthThresh(minLineLengthThreshold);
3168
3169 for (unsigned int j = 0; j < corners_without_duplicates.size(); j++) {
3170 polygon.addPoint(j, corners_without_duplicates[j]);
3171 }
3172
3173 m_projectionErrorFaces.addPolygon(&polygon);
3174
3175 if (clippingFlag != vpPolygon3D::NO_CLIPPING)
3176 m_projectionErrorFaces.getPolygon().back()->setClipping(clippingFlag);
3177
3178 if ((clippingFlag & vpPolygon3D::NEAR_CLIPPING) == vpPolygon3D::NEAR_CLIPPING)
3179 m_projectionErrorFaces.getPolygon().back()->setNearClippingDistance(distNearClip);
3180
3181 if ((clippingFlag & vpPolygon3D::FAR_CLIPPING) == vpPolygon3D::FAR_CLIPPING)
3182 m_projectionErrorFaces.getPolygon().back()->setFarClippingDistance(distFarClip);
3183 }
3184
addProjectionErrorPolygon(const vpPoint & p1,const vpPoint & p2,const vpPoint & p3,double radius,int idFace,const std::string & polygonName,bool useLod,double minPolygonAreaThreshold)3185 void vpMbTracker::addProjectionErrorPolygon(const vpPoint &p1, const vpPoint &p2, const vpPoint &p3, double radius,
3186 int idFace, const std::string &polygonName, bool useLod,
3187 double minPolygonAreaThreshold)
3188 {
3189 vpMbtPolygon polygon;
3190 polygon.setNbPoint(4);
3191 polygon.setName(polygonName);
3192 polygon.setLod(useLod);
3193
3194 polygon.setMinPolygonAreaThresh(minPolygonAreaThreshold);
3195 // Non sense to set minLineLengthThreshold for circle
3196 // but used to be coherent when applying LOD settings for all polygons
3197 polygon.setMinLineLengthThresh(minLineLengthThresholdGeneral);
3198
3199 {
3200 // Create the 4 points of the circle bounding box
3201 vpPlane plane(p1, p2, p3, vpPlane::object_frame);
3202
3203 // Matrice de passage entre world et circle frame
3204 double norm_X = sqrt(vpMath::sqr(p2.get_oX() - p1.get_oX()) + vpMath::sqr(p2.get_oY() - p1.get_oY()) +
3205 vpMath::sqr(p2.get_oZ() - p1.get_oZ()));
3206 double norm_Y = sqrt(vpMath::sqr(plane.getA()) + vpMath::sqr(plane.getB()) + vpMath::sqr(plane.getC()));
3207 vpRotationMatrix wRc;
3208 vpColVector x(3), y(3), z(3);
3209 // X axis is P2-P1
3210 x[0] = (p2.get_oX() - p1.get_oX()) / norm_X;
3211 x[1] = (p2.get_oY() - p1.get_oY()) / norm_X;
3212 x[2] = (p2.get_oZ() - p1.get_oZ()) / norm_X;
3213 // Y axis is the normal of the plane
3214 y[0] = plane.getA() / norm_Y;
3215 y[1] = plane.getB() / norm_Y;
3216 y[2] = plane.getC() / norm_Y;
3217 // Z axis = X ^ Y
3218 z = vpColVector::crossProd(x, y);
3219 for (unsigned int i = 0; i < 3; i++) {
3220 wRc[i][0] = x[i];
3221 wRc[i][1] = y[i];
3222 wRc[i][2] = z[i];
3223 }
3224
3225 vpTranslationVector wtc(p1.get_oX(), p1.get_oY(), p1.get_oZ());
3226 vpHomogeneousMatrix wMc(wtc, wRc);
3227
3228 vpColVector c_p(4); // A point in the circle frame that is on the bbox
3229 c_p[0] = radius;
3230 c_p[1] = 0;
3231 c_p[2] = radius;
3232 c_p[3] = 1;
3233
3234 // Matrix to rotate a point by 90 deg around Y in the circle frame
3235 for (unsigned int i = 0; i < 4; i++) {
3236 vpColVector w_p(4); // A point in the word frame
3237 vpHomogeneousMatrix cMc_90(vpTranslationVector(), vpRotationMatrix(0, vpMath::rad(90 * i), 0));
3238 w_p = wMc * cMc_90 * c_p;
3239
3240 vpPoint w_P;
3241 w_P.setWorldCoordinates(w_p[0], w_p[1], w_p[2]);
3242
3243 polygon.addPoint(i, w_P);
3244 }
3245 }
3246
3247 polygon.setIndex(idFace);
3248 m_projectionErrorFaces.addPolygon(&polygon);
3249
3250 if (clippingFlag != vpPolygon3D::NO_CLIPPING)
3251 m_projectionErrorFaces.getPolygon().back()->setClipping(clippingFlag);
3252
3253 if ((clippingFlag & vpPolygon3D::NEAR_CLIPPING) == vpPolygon3D::NEAR_CLIPPING)
3254 m_projectionErrorFaces.getPolygon().back()->setNearClippingDistance(distNearClip);
3255
3256 if ((clippingFlag & vpPolygon3D::FAR_CLIPPING) == vpPolygon3D::FAR_CLIPPING)
3257 m_projectionErrorFaces.getPolygon().back()->setFarClippingDistance(distFarClip);
3258 }
3259
addProjectionErrorPolygon(const vpPoint & p1,const vpPoint & p2,int idFace,const std::string & polygonName,bool useLod,double minLineLengthThreshold)3260 void vpMbTracker::addProjectionErrorPolygon(const vpPoint &p1, const vpPoint &p2, int idFace, const std::string &polygonName,
3261 bool useLod, double minLineLengthThreshold)
3262 {
3263 // A polygon as a single line that corresponds to the revolution axis of the
3264 // cylinder
3265 vpMbtPolygon polygon;
3266 polygon.setNbPoint(2);
3267
3268 polygon.addPoint(0, p1);
3269 polygon.addPoint(1, p2);
3270
3271 polygon.setIndex(idFace);
3272 polygon.setName(polygonName);
3273 polygon.setLod(useLod);
3274
3275 polygon.setMinLineLengthThresh(minLineLengthThreshold);
3276 // Non sense to set minPolygonAreaThreshold for cylinder
3277 // but used to be coherent when applying LOD settings for all polygons
3278 polygon.setMinPolygonAreaThresh(minPolygonAreaThresholdGeneral);
3279
3280 m_projectionErrorFaces.addPolygon(&polygon);
3281
3282 if (clippingFlag != vpPolygon3D::NO_CLIPPING)
3283 m_projectionErrorFaces.getPolygon().back()->setClipping(clippingFlag);
3284
3285 if ((clippingFlag & vpPolygon3D::NEAR_CLIPPING) == vpPolygon3D::NEAR_CLIPPING)
3286 m_projectionErrorFaces.getPolygon().back()->setNearClippingDistance(distNearClip);
3287
3288 if ((clippingFlag & vpPolygon3D::FAR_CLIPPING) == vpPolygon3D::FAR_CLIPPING)
3289 m_projectionErrorFaces.getPolygon().back()->setFarClippingDistance(distFarClip);
3290 }
3291
addProjectionErrorPolygon(const std::vector<std::vector<vpPoint>> & listFaces,int idFace,const std::string & polygonName,bool useLod,double minLineLengthThreshold)3292 void vpMbTracker::addProjectionErrorPolygon(const std::vector<std::vector<vpPoint> > &listFaces, int idFace,
3293 const std::string &polygonName, bool useLod, double minLineLengthThreshold)
3294 {
3295 int id = idFace;
3296 for (unsigned int i = 0; i < listFaces.size(); i++) {
3297 vpMbtPolygon polygon;
3298 polygon.setNbPoint((unsigned int)listFaces[i].size());
3299 for (unsigned int j = 0; j < listFaces[i].size(); j++)
3300 polygon.addPoint(j, listFaces[i][j]);
3301
3302 polygon.setIndex(id);
3303 polygon.setName(polygonName);
3304 polygon.setIsPolygonOriented(false);
3305 polygon.setLod(useLod);
3306 polygon.setMinLineLengthThresh(minLineLengthThreshold);
3307 polygon.setMinPolygonAreaThresh(minPolygonAreaThresholdGeneral);
3308
3309 m_projectionErrorFaces.addPolygon(&polygon);
3310
3311 if (clippingFlag != vpPolygon3D::NO_CLIPPING)
3312 m_projectionErrorFaces.getPolygon().back()->setClipping(clippingFlag);
3313
3314 if ((clippingFlag & vpPolygon3D::NEAR_CLIPPING) == vpPolygon3D::NEAR_CLIPPING)
3315 m_projectionErrorFaces.getPolygon().back()->setNearClippingDistance(distNearClip);
3316
3317 if ((clippingFlag & vpPolygon3D::FAR_CLIPPING) == vpPolygon3D::FAR_CLIPPING)
3318 m_projectionErrorFaces.getPolygon().back()->setFarClippingDistance(distFarClip);
3319
3320 id++;
3321 }
3322 }
3323
addProjectionErrorLine(vpPoint & P1,vpPoint & P2,int polygon,std::string name)3324 void vpMbTracker::addProjectionErrorLine(vpPoint &P1, vpPoint &P2, int polygon, std::string name)
3325 {
3326 // suppress line already in the model
3327 bool already_here = false;
3328 vpMbtDistanceLine *l;
3329
3330 for (std::vector<vpMbtDistanceLine *>::const_iterator it = m_projectionErrorLines.begin(); it != m_projectionErrorLines.end(); ++it) {
3331 l = *it;
3332 if ((samePoint(*(l->p1), P1) && samePoint(*(l->p2), P2)) ||
3333 (samePoint(*(l->p1), P2) && samePoint(*(l->p2), P1))) {
3334 already_here = true;
3335 l->addPolygon(polygon);
3336 l->hiddenface = &m_projectionErrorFaces;
3337 }
3338 }
3339
3340 if (!already_here) {
3341 l = new vpMbtDistanceLine;
3342
3343 l->setCameraParameters(m_cam);
3344 l->buildFrom(P1, P2, m_rand);
3345 l->addPolygon(polygon);
3346 l->setMovingEdge(&m_projectionErrorMe);
3347 l->hiddenface = &m_projectionErrorFaces;
3348 l->useScanLine = useScanLine;
3349
3350 l->setIndex((unsigned int)m_projectionErrorLines.size());
3351 l->setName(name);
3352
3353 if (clippingFlag != vpPolygon3D::NO_CLIPPING)
3354 l->getPolygon().setClipping(clippingFlag);
3355
3356 if ((clippingFlag & vpPolygon3D::NEAR_CLIPPING) == vpPolygon3D::NEAR_CLIPPING)
3357 l->getPolygon().setNearClippingDistance(distNearClip);
3358
3359 if ((clippingFlag & vpPolygon3D::FAR_CLIPPING) == vpPolygon3D::FAR_CLIPPING)
3360 l->getPolygon().setFarClippingDistance(distFarClip);
3361
3362 m_projectionErrorLines.push_back(l);
3363 }
3364 }
3365
addProjectionErrorCircle(const vpPoint & P1,const vpPoint & P2,const vpPoint & P3,double r,int idFace,const std::string & name)3366 void vpMbTracker::addProjectionErrorCircle(const vpPoint &P1, const vpPoint &P2, const vpPoint &P3, double r, int idFace,
3367 const std::string &name)
3368 {
3369 bool already_here = false;
3370 vpMbtDistanceCircle *ci;
3371
3372 for (std::vector<vpMbtDistanceCircle *>::const_iterator it = m_projectionErrorCircles.begin(); it != m_projectionErrorCircles.end(); ++it) {
3373 ci = *it;
3374 if ((samePoint(*(ci->p1), P1) && samePoint(*(ci->p2), P2) && samePoint(*(ci->p3), P3)) ||
3375 (samePoint(*(ci->p1), P1) && samePoint(*(ci->p2), P3) && samePoint(*(ci->p3), P2))) {
3376 already_here =
3377 (std::fabs(ci->radius - r) < std::numeric_limits<double>::epsilon() * vpMath::maximum(ci->radius, r));
3378 }
3379 }
3380
3381 if (!already_here) {
3382 ci = new vpMbtDistanceCircle;
3383
3384 ci->setCameraParameters(m_cam);
3385 ci->buildFrom(P1, P2, P3, r);
3386 ci->setMovingEdge(&m_projectionErrorMe);
3387 ci->setIndex((unsigned int)m_projectionErrorCircles.size());
3388 ci->setName(name);
3389 ci->index_polygon = idFace;
3390 ci->hiddenface = &m_projectionErrorFaces;
3391
3392 m_projectionErrorCircles.push_back(ci);
3393 }
3394 }
3395
addProjectionErrorCylinder(const vpPoint & P1,const vpPoint & P2,double r,int idFace,const std::string & name)3396 void vpMbTracker::addProjectionErrorCylinder(const vpPoint &P1, const vpPoint &P2, double r, int idFace,
3397 const std::string &name)
3398 {
3399 bool already_here = false;
3400 vpMbtDistanceCylinder *cy;
3401
3402 for (std::vector<vpMbtDistanceCylinder *>::const_iterator it = m_projectionErrorCylinders.begin(); it != m_projectionErrorCylinders.end();
3403 ++it) {
3404 cy = *it;
3405 if ((samePoint(*(cy->p1), P1) && samePoint(*(cy->p2), P2)) ||
3406 (samePoint(*(cy->p1), P2) && samePoint(*(cy->p2), P1))) {
3407 already_here =
3408 (std::fabs(cy->radius - r) < std::numeric_limits<double>::epsilon() * vpMath::maximum(cy->radius, r));
3409 }
3410 }
3411
3412 if (!already_here) {
3413 cy = new vpMbtDistanceCylinder;
3414
3415 cy->setCameraParameters(m_cam);
3416 cy->buildFrom(P1, P2, r);
3417 cy->setMovingEdge(&m_projectionErrorMe);
3418 cy->setIndex((unsigned int)m_projectionErrorCylinders.size());
3419 cy->setName(name);
3420 cy->index_polygon = idFace;
3421 cy->hiddenface = &m_projectionErrorFaces;
3422 m_projectionErrorCylinders.push_back(cy);
3423 }
3424 }
3425
initProjectionErrorCircle(const vpPoint & p1,const vpPoint & p2,const vpPoint & p3,double radius,int idFace,const std::string & name)3426 void vpMbTracker::initProjectionErrorCircle(const vpPoint &p1, const vpPoint &p2, const vpPoint &p3,
3427 double radius, int idFace, const std::string &name)
3428 {
3429 addProjectionErrorCircle(p1, p2, p3, radius, idFace, name);
3430 }
3431
initProjectionErrorCylinder(const vpPoint & p1,const vpPoint & p2,double radius,int idFace,const std::string & name)3432 void vpMbTracker::initProjectionErrorCylinder(const vpPoint &p1, const vpPoint &p2, double radius,
3433 int idFace, const std::string &name)
3434 {
3435 addProjectionErrorCylinder(p1, p2, radius, idFace, name);
3436 }
3437
initProjectionErrorFaceFromCorners(vpMbtPolygon & polygon)3438 void vpMbTracker::initProjectionErrorFaceFromCorners(vpMbtPolygon &polygon)
3439 {
3440 unsigned int nbpt = polygon.getNbPoint();
3441 if (nbpt > 0) {
3442 for (unsigned int i = 0; i < nbpt - 1; i++)
3443 addProjectionErrorLine(polygon.p[i], polygon.p[i + 1], polygon.getIndex(), polygon.getName());
3444 addProjectionErrorLine(polygon.p[nbpt - 1], polygon.p[0], polygon.getIndex(), polygon.getName());
3445 }
3446 }
3447
initProjectionErrorFaceFromLines(vpMbtPolygon & polygon)3448 void vpMbTracker::initProjectionErrorFaceFromLines(vpMbtPolygon &polygon)
3449 {
3450 unsigned int nbpt = polygon.getNbPoint();
3451 if (nbpt > 0) {
3452 for (unsigned int i = 0; i < nbpt - 1; i++)
3453 addProjectionErrorLine(polygon.p[i], polygon.p[i + 1], polygon.getIndex(), polygon.getName());
3454 }
3455 }
3456
3457 /*!
3458 Compute projection error given an input image and camera pose, parameters.
3459 This projection error uses locations sampled exactly where the model is projected using the camera pose
3460 and intrinsic parameters.
3461 You may want to use \sa setProjectionErrorComputation \sa getProjectionError
3462
3463 to get a projection error computed at the ME locations after a call to track().
3464 It works similarly to vpMbTracker::getProjectionError function:
3465 <blockquote>
3466 Get the error angle between the gradient direction of the model features projected at the resulting pose and their normal.
3467 The error is expressed in degree between 0 and 90.
3468 </blockquote>
3469
3470 \param I : Input grayscale image.
3471 \param _cMo : Camera pose.
3472 \param _cam : Camera parameters.
3473 */
computeCurrentProjectionError(const vpImage<unsigned char> & I,const vpHomogeneousMatrix & _cMo,const vpCameraParameters & _cam)3474 double vpMbTracker::computeCurrentProjectionError(const vpImage<unsigned char> &I, const vpHomogeneousMatrix &_cMo,
3475 const vpCameraParameters &_cam)
3476 {
3477 if (!modelInitialised) {
3478 throw vpException(vpException::fatalError, "model not initialized");
3479 }
3480
3481 unsigned int nbFeatures = 0;
3482 double totalProjectionError = computeProjectionErrorImpl(I, _cMo, _cam, nbFeatures);
3483
3484 if (nbFeatures > 0) {
3485 return vpMath::deg(totalProjectionError / (double)nbFeatures);
3486 }
3487
3488 return 90.0;
3489 }
3490
computeProjectionErrorImpl(const vpImage<unsigned char> & I,const vpHomogeneousMatrix & _cMo,const vpCameraParameters & _cam,unsigned int & nbFeatures)3491 double vpMbTracker::computeProjectionErrorImpl(const vpImage<unsigned char> &I, const vpHomogeneousMatrix &_cMo,
3492 const vpCameraParameters &_cam, unsigned int &nbFeatures)
3493 {
3494 bool update_cam = m_projectionErrorCam != _cam;
3495 if (update_cam) {
3496 m_projectionErrorCam = _cam;
3497
3498 for (std::vector<vpMbtDistanceLine *>::const_iterator it = m_projectionErrorLines.begin();
3499 it != m_projectionErrorLines.end(); ++it) {
3500 vpMbtDistanceLine *l = *it;
3501 l->setCameraParameters(m_projectionErrorCam);
3502 }
3503
3504 for (std::vector<vpMbtDistanceCylinder *>::const_iterator it = m_projectionErrorCylinders.begin();
3505 it != m_projectionErrorCylinders.end(); ++it) {
3506 vpMbtDistanceCylinder *cy = *it;
3507 cy->setCameraParameters(m_projectionErrorCam);
3508 }
3509
3510 for (std::vector<vpMbtDistanceCircle *>::const_iterator it = m_projectionErrorCircles.begin();
3511 it != m_projectionErrorCircles.end(); ++it) {
3512 vpMbtDistanceCircle *ci = *it;
3513 ci->setCameraParameters(m_projectionErrorCam);
3514 }
3515 }
3516
3517 #ifdef VISP_HAVE_OGRE
3518 if (useOgre) {
3519 if (update_cam || !m_projectionErrorFaces.isOgreInitialised()) {
3520 m_projectionErrorFaces.setBackgroundSizeOgre(I.getHeight(), I.getWidth());
3521 m_projectionErrorFaces.setOgreShowConfigDialog(m_projectionErrorOgreShowConfigDialog);
3522 m_projectionErrorFaces.initOgre(m_projectionErrorCam);
3523 // Turn off Ogre config dialog display for the next call to this
3524 // function since settings are saved in the ogre.cfg file and used
3525 // during the next call
3526 m_projectionErrorOgreShowConfigDialog = false;
3527 }
3528 }
3529 #endif
3530
3531 if (clippingFlag > 2)
3532 m_projectionErrorCam.computeFov(I.getWidth(), I.getHeight());
3533
3534 projectionErrorVisibleFace(I.getWidth(), I.getHeight(), _cMo);
3535
3536 projectionErrorResetMovingEdges();
3537
3538 if (useScanLine) {
3539 if (clippingFlag <= 2)
3540 m_projectionErrorCam.computeFov(I.getWidth(), I.getHeight());
3541
3542 m_projectionErrorFaces.computeClippedPolygons(_cMo, m_projectionErrorCam);
3543 m_projectionErrorFaces.computeScanLineRender(m_projectionErrorCam, I.getWidth(), I.getHeight());
3544 }
3545
3546 projectionErrorInitMovingEdge(I, _cMo);
3547
3548 double totalProjectionError = 0.0;
3549 for (std::vector<vpMbtDistanceLine *>::const_iterator it = m_projectionErrorLines.begin(); it != m_projectionErrorLines.end();
3550 ++it) {
3551 vpMbtDistanceLine *l = *it;
3552 if (l->isVisible() && l->isTracked()) {
3553 for (size_t a = 0; a < l->meline.size(); a++) {
3554 if (l->meline[a] != NULL) {
3555 double lineNormGradient;
3556 unsigned int lineNbFeatures;
3557 l->meline[a]->computeProjectionError(I, lineNormGradient, lineNbFeatures, m_SobelX, m_SobelY,
3558 m_projectionErrorDisplay, m_projectionErrorDisplayLength,
3559 m_projectionErrorDisplayThickness);
3560 totalProjectionError += lineNormGradient;
3561 nbFeatures += lineNbFeatures;
3562 }
3563 }
3564 }
3565 }
3566
3567 for (std::vector<vpMbtDistanceCylinder *>::const_iterator it = m_projectionErrorCylinders.begin();
3568 it != m_projectionErrorCylinders.end(); ++it) {
3569 vpMbtDistanceCylinder *cy = *it;
3570 if (cy->isVisible() && cy->isTracked()) {
3571 if (cy->meline1 != NULL) {
3572 double cylinderNormGradient = 0;
3573 unsigned int cylinderNbFeatures = 0;
3574 cy->meline1->computeProjectionError(I, cylinderNormGradient, cylinderNbFeatures, m_SobelX, m_SobelY,
3575 m_projectionErrorDisplay, m_projectionErrorDisplayLength,
3576 m_projectionErrorDisplayThickness);
3577 totalProjectionError += cylinderNormGradient;
3578 nbFeatures += cylinderNbFeatures;
3579 }
3580
3581 if (cy->meline2 != NULL) {
3582 double cylinderNormGradient = 0;
3583 unsigned int cylinderNbFeatures = 0;
3584 cy->meline2->computeProjectionError(I, cylinderNormGradient, cylinderNbFeatures, m_SobelX, m_SobelY,
3585 m_projectionErrorDisplay, m_projectionErrorDisplayLength,
3586 m_projectionErrorDisplayThickness);
3587 totalProjectionError += cylinderNormGradient;
3588 nbFeatures += cylinderNbFeatures;
3589 }
3590 }
3591 }
3592
3593 for (std::vector<vpMbtDistanceCircle *>::const_iterator it = m_projectionErrorCircles.begin();
3594 it != m_projectionErrorCircles.end(); ++it) {
3595 vpMbtDistanceCircle *c = *it;
3596 if (c->isVisible() && c->isTracked() && c->meEllipse != NULL) {
3597 double circleNormGradient = 0;
3598 unsigned int circleNbFeatures = 0;
3599 c->meEllipse->computeProjectionError(I, circleNormGradient, circleNbFeatures, m_SobelX, m_SobelY,
3600 m_projectionErrorDisplay, m_projectionErrorDisplayLength,
3601 m_projectionErrorDisplayThickness);
3602 totalProjectionError += circleNormGradient;
3603 nbFeatures += circleNbFeatures;
3604 }
3605 }
3606
3607 return totalProjectionError;
3608 }
3609
projectionErrorVisibleFace(unsigned int width,unsigned int height,const vpHomogeneousMatrix & _cMo)3610 void vpMbTracker::projectionErrorVisibleFace(unsigned int width, unsigned int height, const vpHomogeneousMatrix &_cMo)
3611 {
3612 bool changed = false;
3613
3614 if (!useOgre) {
3615 m_projectionErrorFaces.setVisible(width, height, m_projectionErrorCam, _cMo, angleAppears, angleDisappears, changed);
3616 } else {
3617 #ifdef VISP_HAVE_OGRE
3618 m_projectionErrorFaces.setVisibleOgre(width, height, m_projectionErrorCam, _cMo, angleAppears, angleDisappears, changed);
3619 #else
3620 m_projectionErrorFaces.setVisible(width, height, m_projectionErrorCam, _cMo, angleAppears, angleDisappears, changed);
3621 #endif
3622 }
3623 }
3624
projectionErrorResetMovingEdges()3625 void vpMbTracker::projectionErrorResetMovingEdges()
3626 {
3627 for (std::vector<vpMbtDistanceLine *>::const_iterator it = m_projectionErrorLines.begin(); it != m_projectionErrorLines.end(); ++it) {
3628 for (size_t a = 0; a < (*it)->meline.size(); a++) {
3629 if ((*it)->meline[a] != NULL) {
3630 delete (*it)->meline[a];
3631 (*it)->meline[a] = NULL;
3632 }
3633 }
3634
3635 (*it)->meline.clear();
3636 (*it)->nbFeature.clear();
3637 (*it)->nbFeatureTotal = 0;
3638 }
3639
3640 for (std::vector<vpMbtDistanceCylinder *>::const_iterator it = m_projectionErrorCylinders.begin(); it != m_projectionErrorCylinders.end();
3641 ++it) {
3642 if ((*it)->meline1 != NULL) {
3643 delete (*it)->meline1;
3644 (*it)->meline1 = NULL;
3645 }
3646 if ((*it)->meline2 != NULL) {
3647 delete (*it)->meline2;
3648 (*it)->meline2 = NULL;
3649 }
3650
3651 (*it)->nbFeature = 0;
3652 (*it)->nbFeaturel1 = 0;
3653 (*it)->nbFeaturel2 = 0;
3654 }
3655
3656 for (std::vector<vpMbtDistanceCircle *>::const_iterator it = m_projectionErrorCircles.begin(); it != m_projectionErrorCircles.end(); ++it) {
3657 if ((*it)->meEllipse != NULL) {
3658 delete (*it)->meEllipse;
3659 (*it)->meEllipse = NULL;
3660 }
3661 (*it)->nbFeature = 0;
3662 }
3663 }
3664
projectionErrorInitMovingEdge(const vpImage<unsigned char> & I,const vpHomogeneousMatrix & _cMo)3665 void vpMbTracker::projectionErrorInitMovingEdge(const vpImage<unsigned char> &I, const vpHomogeneousMatrix &_cMo)
3666 {
3667 const bool doNotTrack = true;
3668
3669 for (std::vector<vpMbtDistanceLine *>::const_iterator it = m_projectionErrorLines.begin();
3670 it != m_projectionErrorLines.end(); ++it) {
3671 vpMbtDistanceLine *l = *it;
3672 bool isvisible = false;
3673
3674 for (std::list<int>::const_iterator itindex = l->Lindex_polygon.begin(); itindex != l->Lindex_polygon.end();
3675 ++itindex) {
3676 int index = *itindex;
3677 if (index == -1)
3678 isvisible = true;
3679 else {
3680 if (l->hiddenface->isVisible((unsigned int)index))
3681 isvisible = true;
3682 }
3683 }
3684
3685 // Si la ligne n'appartient a aucune face elle est tout le temps visible
3686 if (l->Lindex_polygon.empty())
3687 isvisible = true; // Not sure that this can occur
3688
3689 if (isvisible) {
3690 l->setVisible(true);
3691 l->updateTracked();
3692 if (l->meline.empty() && l->isTracked())
3693 l->initMovingEdge(I, _cMo, doNotTrack, m_mask);
3694 } else {
3695 l->setVisible(false);
3696 for (size_t a = 0; a < l->meline.size(); a++) {
3697 if (l->meline[a] != NULL)
3698 delete l->meline[a];
3699 if (a < l->nbFeature.size())
3700 l->nbFeature[a] = 0;
3701 }
3702 l->nbFeatureTotal = 0;
3703 l->meline.clear();
3704 l->nbFeature.clear();
3705 }
3706 }
3707
3708 for (std::vector<vpMbtDistanceCylinder *>::const_iterator it = m_projectionErrorCylinders.begin();
3709 it != m_projectionErrorCylinders.end(); ++it) {
3710 vpMbtDistanceCylinder *cy = *it;
3711
3712 bool isvisible = false;
3713
3714 int index = cy->index_polygon;
3715 if (index == -1)
3716 isvisible = true;
3717 else {
3718 if (cy->hiddenface->isVisible((unsigned int)index + 1) || cy->hiddenface->isVisible((unsigned int)index + 2) ||
3719 cy->hiddenface->isVisible((unsigned int)index + 3) || cy->hiddenface->isVisible((unsigned int)index + 4))
3720 isvisible = true;
3721 }
3722
3723 if (isvisible) {
3724 cy->setVisible(true);
3725 if (cy->meline1 == NULL || cy->meline2 == NULL) {
3726 if (cy->isTracked())
3727 cy->initMovingEdge(I, _cMo, doNotTrack, m_mask);
3728 }
3729 } else {
3730 cy->setVisible(false);
3731 if (cy->meline1 != NULL)
3732 delete cy->meline1;
3733 if (cy->meline2 != NULL)
3734 delete cy->meline2;
3735 cy->meline1 = NULL;
3736 cy->meline2 = NULL;
3737 cy->nbFeature = 0;
3738 cy->nbFeaturel1 = 0;
3739 cy->nbFeaturel2 = 0;
3740 }
3741 }
3742
3743 for (std::vector<vpMbtDistanceCircle *>::const_iterator it = m_projectionErrorCircles.begin();
3744 it != m_projectionErrorCircles.end(); ++it) {
3745 vpMbtDistanceCircle *ci = *it;
3746 bool isvisible = false;
3747
3748 int index = ci->index_polygon;
3749 if (index == -1)
3750 isvisible = true;
3751 else {
3752 if (ci->hiddenface->isVisible((unsigned int)index))
3753 isvisible = true;
3754 }
3755
3756 if (isvisible) {
3757 ci->setVisible(true);
3758 if (ci->meEllipse == NULL) {
3759 if (ci->isTracked())
3760 ci->initMovingEdge(I, _cMo, doNotTrack, m_mask);
3761 }
3762 } else {
3763 ci->setVisible(false);
3764 if (ci->meEllipse != NULL)
3765 delete ci->meEllipse;
3766 ci->meEllipse = NULL;
3767 ci->nbFeature = 0;
3768 }
3769 }
3770 }
3771
loadConfigFile(const std::string & configFile,bool verbose)3772 void vpMbTracker::loadConfigFile(const std::string &configFile, bool verbose)
3773 {
3774 vpMbtXmlGenericParser xmlp(vpMbtXmlGenericParser::PROJECTION_ERROR_PARSER);
3775 xmlp.setVerbose(verbose);
3776 xmlp.setProjectionErrorMe(m_projectionErrorMe);
3777 xmlp.setProjectionErrorKernelSize(m_projectionErrorKernelSize);
3778
3779 try {
3780 if (verbose) {
3781 std::cout << " *********** Parsing XML for ME projection error ************ " << std::endl;
3782 }
3783 xmlp.parse(configFile);
3784 } catch (...) {
3785 throw vpException(vpException::ioError, "Cannot open XML file \"%s\"", configFile.c_str());
3786 }
3787
3788 vpMe meParser;
3789 xmlp.getProjectionErrorMe(meParser);
3790
3791 setProjectionErrorMovingEdge(meParser);
3792 setProjectionErrorKernelSize(xmlp.getProjectionErrorKernelSize());
3793 }
3794
3795 /*!
3796 Set Moving-Edges parameters for projection error computation.
3797
3798 \param me : Moving-Edges parameters.
3799 */
setProjectionErrorMovingEdge(const vpMe & me)3800 void vpMbTracker::setProjectionErrorMovingEdge(const vpMe &me)
3801 {
3802 m_projectionErrorMe = me;
3803
3804 for (std::vector<vpMbtDistanceLine *>::const_iterator it = m_projectionErrorLines.begin(); it != m_projectionErrorLines.end(); ++it) {
3805 vpMbtDistanceLine *l = *it;
3806 l->setMovingEdge(&m_projectionErrorMe);
3807 }
3808
3809 for (std::vector<vpMbtDistanceCylinder *>::const_iterator it = m_projectionErrorCylinders.begin(); it != m_projectionErrorCylinders.end();
3810 ++it) {
3811 vpMbtDistanceCylinder *cy = *it;
3812 cy->setMovingEdge(&m_projectionErrorMe);
3813 }
3814
3815 for (std::vector<vpMbtDistanceCircle *>::const_iterator it = m_projectionErrorCircles.begin(); it != m_projectionErrorCircles.end(); ++it) {
3816 vpMbtDistanceCircle *ci = *it;
3817 ci->setMovingEdge(&m_projectionErrorMe);
3818 }
3819 }
3820
3821 /*!
3822 Set kernel size used for projection error computation.
3823
3824 \param size : Kernel size computed as kernel_size = size*2 + 1.
3825 */
setProjectionErrorKernelSize(const unsigned int & size)3826 void vpMbTracker::setProjectionErrorKernelSize(const unsigned int &size)
3827 {
3828 m_projectionErrorKernelSize = size;
3829
3830 m_SobelX.resize(size*2+1, size*2+1, false, false);
3831 vpImageFilter::getSobelKernelX(m_SobelX.data, size);
3832
3833 m_SobelY.resize(size*2+1, size*2+1, false, false);
3834 vpImageFilter::getSobelKernelY(m_SobelY.data, size);
3835 }
3836
3837