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 * Klt cylinder, containing points of interest.
33 *
34 * Authors:
35 * Aurelien Yol
36 *
37 *****************************************************************************/
38
39 #include <visp3/core/vpPolygon.h>
40 #include <visp3/mbt/vpMbtDistanceKltCylinder.h>
41 #include <visp3/mbt/vpMbtDistanceKltPoints.h>
42
43 #if defined(VISP_HAVE_MODULE_KLT) && (defined(VISP_HAVE_OPENCV) && (VISP_HAVE_OPENCV_VERSION >= 0x020100))
44
45 #if defined(VISP_HAVE_CLIPPER)
46 #include <clipper.hpp> // clipper private library
47 #endif
48
49 #if defined(__APPLE__) && defined(__MACH__) // Apple OSX and iOS (Darwin)
50 #include <TargetConditionals.h> // To detect OSX or IOS using TARGET_OS_IPHONE or TARGET_OS_IOS macro
51 #endif
52
53 /*!
54 Basic constructor.
55
56 */
vpMbtDistanceKltCylinder()57 vpMbtDistanceKltCylinder::vpMbtDistanceKltCylinder()
58 : c0Mo(), p1Ext(), p2Ext(), cylinder(), circle1(), circle2(), initPoints(), initPoints3D(), curPoints(),
59 curPointsInd(), nbPointsCur(0), nbPointsInit(0), minNbPoint(4), enoughPoints(false), cam(),
60 isTrackedKltCylinder(true), listIndicesCylinderBBox(), hiddenface(NULL), useScanLine(false)
61 {
62 }
63
64 /*!
65 Basic destructor.
66
67 */
~vpMbtDistanceKltCylinder()68 vpMbtDistanceKltCylinder::~vpMbtDistanceKltCylinder() {}
69
buildFrom(const vpPoint & p1,const vpPoint & p2,const double & r)70 void vpMbtDistanceKltCylinder::buildFrom(const vpPoint &p1, const vpPoint &p2, const double &r)
71 {
72 p1Ext = p1;
73 p2Ext = p2;
74
75 vpColVector ABC(3);
76 vpColVector V1(3);
77 vpColVector V2(3);
78
79 V1[0] = p1.get_oX();
80 V1[1] = p1.get_oY();
81 V1[2] = p1.get_oZ();
82 V2[0] = p2.get_oX();
83 V2[1] = p2.get_oY();
84 V2[2] = p2.get_oZ();
85
86 // Get the axis of the cylinder
87 ABC = V1 - V2;
88
89 // Build our extremity circles
90 circle1.setWorldCoordinates(ABC[0], ABC[1], ABC[2], p1.get_oX(), p1.get_oY(), p1.get_oZ(), r);
91 circle2.setWorldCoordinates(ABC[0], ABC[1], ABC[2], p2.get_oX(), p2.get_oY(), p2.get_oZ(), r);
92
93 // Build our cylinder
94 cylinder.setWorldCoordinates(ABC[0], ABC[1], ABC[2], (p1.get_oX() + p2.get_oX()) / 2.0,
95 (p1.get_oY() + p2.get_oY()) / 2.0, (p1.get_oZ() + p2.get_oZ()) / 2.0, r);
96 }
97
98 /*!
99 Initialise the cylinder to track. All the points in the map, representing
100 all the map detected in the image, are parsed in order to extract the id of
101 the points that are indeed in the face.
102
103 \param _tracker : ViSP OpenCV KLT Tracker.
104 \param cMo : Pose of the object in the camera frame at initialization.
105 */
init(const vpKltOpencv & _tracker,const vpHomogeneousMatrix & cMo)106 void vpMbtDistanceKltCylinder::init(const vpKltOpencv &_tracker, const vpHomogeneousMatrix &cMo)
107 {
108 c0Mo = cMo;
109 cylinder.changeFrame(cMo);
110
111 // extract ids of the points in the face
112 nbPointsInit = 0;
113 nbPointsCur = 0;
114 initPoints = std::map<int, vpImagePoint>();
115 initPoints3D = std::map<int, vpPoint>();
116 curPoints = std::map<int, vpImagePoint>();
117 curPointsInd = std::map<int, int>();
118
119 for (unsigned int i = 0; i < static_cast<unsigned int>(_tracker.getNbFeatures()); i++) {
120 long id;
121 float x_tmp, y_tmp;
122 _tracker.getFeature((int)i, id, x_tmp, y_tmp);
123
124 bool add = false;
125
126 if (useScanLine) {
127 if ((unsigned int)y_tmp < hiddenface->getMbScanLineRenderer().getPrimitiveIDs().getHeight() &&
128 (unsigned int)x_tmp < hiddenface->getMbScanLineRenderer().getPrimitiveIDs().getWidth()) {
129 for (unsigned int kc = 0; kc < listIndicesCylinderBBox.size(); kc++)
130 if (hiddenface->getMbScanLineRenderer().getPrimitiveIDs()[(unsigned int)y_tmp][(unsigned int)x_tmp] ==
131 listIndicesCylinderBBox[kc]) {
132 add = true;
133 break;
134 }
135 }
136 } else {
137 std::vector<vpImagePoint> roi;
138 for (unsigned int kc = 0; kc < listIndicesCylinderBBox.size(); kc++) {
139 hiddenface->getPolygon()[(size_t)listIndicesCylinderBBox[kc]]->getRoiClipped(cam, roi);
140 if (vpPolygon::isInside(roi, y_tmp, x_tmp)) {
141 add = true;
142 break;
143 }
144 roi.clear();
145 }
146 }
147
148 if (add) {
149
150 double xm = 0, ym = 0;
151 vpPixelMeterConversion::convertPoint(cam, x_tmp, y_tmp, xm, ym);
152 double Z = computeZ(xm, ym);
153 if (!vpMath::isNaN(Z)) {
154 #if TARGET_OS_IPHONE
155 initPoints[(int)id] = vpImagePoint(y_tmp, x_tmp);
156 curPoints[(int)id] = vpImagePoint(y_tmp, x_tmp);
157 curPointsInd[(int)id] = (int)i;
158 #else
159 initPoints[id] = vpImagePoint(y_tmp, x_tmp);
160 curPoints[id] = vpImagePoint(y_tmp, x_tmp);
161 curPointsInd[id] = (int)i;
162 #endif
163 nbPointsInit++;
164 nbPointsCur++;
165
166 vpPoint p;
167 p.setWorldCoordinates(xm * Z, ym * Z, Z);
168 #if TARGET_OS_IPHONE
169 initPoints3D[(int)id] = p;
170 #else
171 initPoints3D[id] = p;
172 #endif
173 // std::cout << "Computed Z for : " << xm << "," << ym << " : " <<
174 // computeZ(xm,ym) << std::endl;
175 }
176 }
177 }
178
179 if (nbPointsCur >= minNbPoint)
180 enoughPoints = true;
181 else
182 enoughPoints = false;
183
184 // std::cout << "Nb detected points in cylinder : " << nbPointsCur <<
185 // std::endl;
186 }
187
188 /*!
189 compute the number of point in this instanciation of the tracker that
190 corresponds to the points of the cylinder
191
192 \param _tracker : the KLT tracker
193 \return the number of points that are tracked in this face and in this
194 instanciation of the tracker
195 */
computeNbDetectedCurrent(const vpKltOpencv & _tracker)196 unsigned int vpMbtDistanceKltCylinder::computeNbDetectedCurrent(const vpKltOpencv &_tracker)
197 {
198 long id;
199 float x, y;
200 nbPointsCur = 0;
201 curPoints = std::map<int, vpImagePoint>();
202 curPointsInd = std::map<int, int>();
203
204 for (unsigned int i = 0; i < static_cast<unsigned int>(_tracker.getNbFeatures()); i++) {
205 _tracker.getFeature((int)i, id, x, y);
206 if (isTrackedFeature((int)id)) {
207 #if TARGET_OS_IPHONE
208 curPoints[(int)id] = vpImagePoint(static_cast<double>(y), static_cast<double>(x));
209 curPointsInd[(int)id] = (int)i;
210 #else
211 curPoints[id] = vpImagePoint(static_cast<double>(y), static_cast<double>(x));
212 curPointsInd[id] = (int)i;
213 #endif
214 nbPointsCur++;
215 }
216 }
217
218 if (nbPointsCur >= minNbPoint)
219 enoughPoints = true;
220 else
221 enoughPoints = false;
222
223 return nbPointsCur;
224 }
225
226 /*!
227 This method removes the outliers. A point is considered as outlier when its
228 associated weight is below a given threshold (threshold_outlier).
229
230 \param _w : Vector containing the weight of all the tracked points.
231 \param threshold_outlier : Threshold to specify wether or not a point has to
232 be deleted.
233 */
removeOutliers(const vpColVector & _w,const double & threshold_outlier)234 void vpMbtDistanceKltCylinder::removeOutliers(const vpColVector &_w, const double &threshold_outlier)
235 {
236 std::map<int, vpImagePoint> tmp;
237 std::map<int, int> tmp2;
238 unsigned int nbSupp = 0;
239 unsigned int k = 0;
240
241 nbPointsCur = 0;
242 std::map<int, vpImagePoint>::const_iterator iter = curPoints.begin();
243 for (; iter != curPoints.end(); ++iter) {
244 if (_w[k] > threshold_outlier && _w[k + 1] > threshold_outlier) {
245 // if(_w[k] > threshold_outlier || _w[k+1] > threshold_outlier){
246 tmp[iter->first] = vpImagePoint(iter->second.get_i(), iter->second.get_j());
247 tmp2[iter->first] = curPointsInd[iter->first];
248 nbPointsCur++;
249 } else {
250 nbSupp++;
251 initPoints.erase(iter->first);
252 }
253
254 k += 2;
255 }
256
257 if (nbSupp != 0) {
258 curPoints = std::map<int, vpImagePoint>();
259 curPointsInd = std::map<int, int>();
260
261 curPoints = tmp;
262 curPointsInd = tmp2;
263 if (nbPointsCur >= minNbPoint)
264 enoughPoints = true;
265 else
266 enoughPoints = false;
267 }
268 }
269
270 /*!
271 Compute the interaction matrix and the residu vector for the face.
272 The method assumes that these two objects are properly sized in order to be
273 able to improve the speed with the use of SubCoVector and subMatrix.
274
275 \warning The function preCompute must be called before the this method.
276
277 \param _cMc0 : camera displacement since initialisation
278 \param _R : the residu vector
279 \param _J : the interaction matrix
280 */
computeInteractionMatrixAndResidu(const vpHomogeneousMatrix & _cMc0,vpColVector & _R,vpMatrix & _J)281 void vpMbtDistanceKltCylinder::computeInteractionMatrixAndResidu(const vpHomogeneousMatrix &_cMc0, vpColVector &_R,
282 vpMatrix &_J)
283 {
284 unsigned int index_ = 0;
285
286 cylinder.changeFrame(_cMc0 * c0Mo);
287
288 std::map<int, vpImagePoint>::const_iterator iter = curPoints.begin();
289 for (; iter != curPoints.end(); ++iter) {
290 int id(iter->first);
291 double i_cur(iter->second.get_i()), j_cur(iter->second.get_j());
292
293 double x_cur(0), y_cur(0);
294 vpPixelMeterConversion::convertPoint(cam, j_cur, i_cur, x_cur, y_cur);
295
296 vpPoint p0 = initPoints3D[id];
297 p0.changeFrame(_cMc0);
298 p0.project();
299
300 double x0_transform(p0.get_x()), y0_transform(p0.get_y());
301
302 double Z = computeZ(x_cur, y_cur);
303
304 if (vpMath::isNaN(Z) || Z < std::numeric_limits<double>::epsilon()) {
305 // std::cout << "Z is Nan : " << A << " , " << B << " , " << C << "
306 // | " << Z << " | " << x_cur << " , " << y_cur << std::endl;
307 // std::cout << std::sqrt(B*B - A*C) << " , " << B*B - A*C <<
308 // std::endl;
309
310 _J[2 * index_][0] = 0;
311 _J[2 * index_][1] = 0;
312 _J[2 * index_][2] = 0;
313 _J[2 * index_][3] = 0;
314 _J[2 * index_][4] = 0;
315 _J[2 * index_][5] = 0;
316
317 _J[2 * index_ + 1][0] = 0;
318 _J[2 * index_ + 1][1] = 0;
319 _J[2 * index_ + 1][2] = 0;
320 _J[2 * index_ + 1][3] = 0;
321 _J[2 * index_ + 1][4] = 0;
322 _J[2 * index_ + 1][5] = 0;
323
324 _R[2 * index_] = (x0_transform - x_cur);
325 _R[2 * index_ + 1] = (y0_transform - y_cur);
326 index_++;
327 } else {
328 double invZ = 1.0 / Z;
329
330 _J[2 * index_][0] = -invZ;
331 _J[2 * index_][1] = 0;
332 _J[2 * index_][2] = x_cur * invZ;
333 _J[2 * index_][3] = x_cur * y_cur;
334 _J[2 * index_][4] = -(1 + x_cur * x_cur);
335 _J[2 * index_][5] = y_cur;
336
337 _J[2 * index_ + 1][0] = 0;
338 _J[2 * index_ + 1][1] = -invZ;
339 _J[2 * index_ + 1][2] = y_cur * invZ;
340 _J[2 * index_ + 1][3] = (1 + y_cur * y_cur);
341 _J[2 * index_ + 1][4] = -y_cur * x_cur;
342 _J[2 * index_ + 1][5] = -x_cur;
343
344 _R[2 * index_] = (x0_transform - x_cur);
345 _R[2 * index_ + 1] = (y0_transform - y_cur);
346 index_++;
347 }
348 }
349 }
350
351 /*!
352 Test whether the feature with identifier id in paramters is in the list of
353 tracked features.
354
355 \param _id : the id of the current feature to test
356 \return true if the id is in the list of tracked feature
357 */
isTrackedFeature(int _id)358 bool vpMbtDistanceKltCylinder::isTrackedFeature(int _id)
359 {
360 std::map<int, vpImagePoint>::iterator iter = initPoints.find(_id);
361 if (iter != initPoints.end())
362 return true;
363
364 return false;
365 }
366
367 /*!
368 Modification of all the pixels that are in the roi to the value of _nb (
369 default is 255).
370
371 \param mask : the mask to update (0, not in the object, _nb otherwise).
372 \param nb : Optionnal value to set to the pixels included in the face.
373 \param shiftBorder : Optionnal shift for the border in pixel (sort of
374 built-in erosion) to avoid to consider pixels near the limits of the face.
375 */
updateMask(cv::Mat & mask,unsigned char nb,unsigned int shiftBorder)376 void vpMbtDistanceKltCylinder::updateMask(
377 #if (VISP_HAVE_OPENCV_VERSION >= 0x020408)
378 cv::Mat &mask,
379 #else
380 IplImage *mask,
381 #endif
382 unsigned char nb, unsigned int shiftBorder)
383 {
384 #if (VISP_HAVE_OPENCV_VERSION >= 0x020408)
385 int width = mask.cols;
386 int height = mask.rows;
387 #else
388 int width = mask->width;
389 int height = mask->height;
390 #endif
391
392 for (unsigned int kc = 0; kc < listIndicesCylinderBBox.size(); kc++) {
393 if ((*hiddenface)[(unsigned int)listIndicesCylinderBBox[kc]]->isVisible() &&
394 (*hiddenface)[(unsigned int)listIndicesCylinderBBox[kc]]->getNbPoint() > 2) {
395 int i_min, i_max, j_min, j_max;
396 std::vector<vpImagePoint> roi;
397 (*hiddenface)[(unsigned int)listIndicesCylinderBBox[kc]]->getRoiClipped(cam, roi);
398
399 double shiftBorder_d = (double)shiftBorder;
400 #if defined(VISP_HAVE_CLIPPER)
401 std::vector<vpImagePoint> roi_offset;
402
403 ClipperLib::Path path;
404 for (std::vector<vpImagePoint>::const_iterator it = roi.begin(); it != roi.end(); ++it) {
405 path.push_back(ClipperLib::IntPoint((ClipperLib::cInt)it->get_u(), (ClipperLib::cInt)it->get_v()));
406 }
407
408 ClipperLib::Paths solution;
409 ClipperLib::ClipperOffset co;
410 co.AddPath(path, ClipperLib::jtRound, ClipperLib::etClosedPolygon);
411 co.Execute(solution, -shiftBorder_d);
412
413 // Keep biggest polygon by area
414 if (!solution.empty()) {
415 size_t index_max = 0;
416
417 if (solution.size() > 1) {
418 double max_area = 0;
419 vpPolygon polygon_area;
420
421 for (size_t i = 0; i < solution.size(); i++) {
422 std::vector<vpImagePoint> corners;
423
424 for (size_t j = 0; j < solution[i].size(); j++) {
425 corners.push_back(vpImagePoint((double)(solution[i][j].Y), (double)(solution[i][j].X)));
426 }
427
428 polygon_area.buildFrom(corners);
429 if (polygon_area.getArea() > max_area) {
430 max_area = polygon_area.getArea();
431 index_max = i;
432 }
433 }
434 }
435
436 for (size_t i = 0; i < solution[index_max].size(); i++) {
437 roi_offset.push_back(vpImagePoint((double)(solution[index_max][i].Y), (double)(solution[index_max][i].X)));
438 }
439 } else {
440 roi_offset = roi;
441 }
442
443 vpPolygon polygon_test(roi_offset);
444 vpImagePoint imPt;
445 #endif
446
447 #if defined(VISP_HAVE_CLIPPER)
448 vpPolygon3D::getMinMaxRoi(roi_offset, i_min, i_max, j_min, j_max);
449 #else
450 vpPolygon3D::getMinMaxRoi(roi, i_min, i_max, j_min, j_max);
451 #endif
452
453 /* check image boundaries */
454 if (i_min > height) { // underflow
455 i_min = 0;
456 }
457 if (i_max > height) {
458 i_max = height;
459 }
460 if (j_min > width) { // underflow
461 j_min = 0;
462 }
463 if (j_max > width) {
464 j_max = width;
465 }
466
467 #if (VISP_HAVE_OPENCV_VERSION >= 0x020408)
468 for (int i = i_min; i < i_max; i++) {
469 double i_d = (double)i;
470
471 for (int j = j_min; j < j_max; j++) {
472 double j_d = (double)j;
473
474 #if defined(VISP_HAVE_CLIPPER)
475 imPt.set_ij(i_d, j_d);
476 if (polygon_test.isInside(imPt)) {
477 mask.ptr<uchar>(i)[j] = nb;
478 }
479 #else
480 if (shiftBorder != 0) {
481 if (vpPolygon::isInside(roi, i_d, j_d) &&
482 vpPolygon::isInside(roi, i_d + shiftBorder_d, j_d + shiftBorder_d) &&
483 vpPolygon::isInside(roi, i_d - shiftBorder_d, j_d + shiftBorder_d) &&
484 vpPolygon::isInside(roi, i_d + shiftBorder_d, j_d - shiftBorder_d) &&
485 vpPolygon::isInside(roi, i_d - shiftBorder_d, j_d - shiftBorder_d)) {
486 mask.at<unsigned char>(i, j) = nb;
487 }
488 } else {
489 if (vpPolygon::isInside(roi, i, j)) {
490 mask.at<unsigned char>(i, j) = nb;
491 }
492 }
493 #endif
494 }
495 }
496 #else
497 unsigned char *ptrData = (unsigned char *)mask->imageData + i_min * mask->widthStep + j_min;
498 for (int i = i_min; i < i_max; i++) {
499 double i_d = (double)i;
500 for (int j = j_min; j < j_max; j++) {
501 double j_d = (double)j;
502 if (shiftBorder != 0) {
503 if (vpPolygon::isInside(roi, i_d, j_d) &&
504 vpPolygon::isInside(roi, i_d + shiftBorder_d, j_d + shiftBorder_d) &&
505 vpPolygon::isInside(roi, i_d - shiftBorder_d, j_d + shiftBorder_d) &&
506 vpPolygon::isInside(roi, i_d + shiftBorder_d, j_d - shiftBorder_d) &&
507 vpPolygon::isInside(roi, i_d - shiftBorder_d, j_d - shiftBorder_d)) {
508 *(ptrData++) = nb;
509 } else {
510 ptrData++;
511 }
512 } else {
513 if (vpPolygon::isInside(roi, i, j)) {
514 *(ptrData++) = nb;
515 } else {
516 ptrData++;
517 }
518 }
519 }
520 ptrData += mask->widthStep - j_max + j_min;
521 }
522 #endif
523 }
524 }
525 }
526
527 /*!
528 Display the primitives tracked for the cylinder.
529
530 \param _I : The image where to display.
531 */
displayPrimitive(const vpImage<unsigned char> & _I)532 void vpMbtDistanceKltCylinder::displayPrimitive(const vpImage<unsigned char> &_I)
533 {
534 std::map<int, vpImagePoint>::const_iterator iter = curPoints.begin();
535 for (; iter != curPoints.end(); ++iter) {
536 int id(iter->first);
537 vpImagePoint iP;
538 iP.set_i(static_cast<double>(iter->second.get_i()));
539 iP.set_j(static_cast<double>(iter->second.get_j()));
540
541 vpDisplay::displayCross(_I, iP, 10, vpColor::red);
542
543 iP.set_i(vpMath::round(iP.get_i() + 7));
544 iP.set_j(vpMath::round(iP.get_j() + 7));
545 std::stringstream ss;
546 ss << id;
547 vpDisplay::displayText(_I, iP, ss.str(), vpColor::red);
548 }
549 }
550
551 /*!
552 Display the primitives tracked for the cylinder.
553
554 \param _I : The image where to display.
555 */
displayPrimitive(const vpImage<vpRGBa> & _I)556 void vpMbtDistanceKltCylinder::displayPrimitive(const vpImage<vpRGBa> &_I)
557 {
558 std::map<int, vpImagePoint>::const_iterator iter = curPoints.begin();
559 for (; iter != curPoints.end(); ++iter) {
560 int id(iter->first);
561 vpImagePoint iP;
562 iP.set_i(static_cast<double>(iter->second.get_i()));
563 iP.set_j(static_cast<double>(iter->second.get_j()));
564
565 vpDisplay::displayCross(_I, iP, 10, vpColor::red);
566
567 iP.set_i(vpMath::round(iP.get_i() + 7));
568 iP.set_j(vpMath::round(iP.get_j() + 7));
569 std::stringstream ss;
570 ss << id;
571 vpDisplay::displayText(_I, iP, ss.str(), vpColor::red);
572 }
573 }
574
display(const vpImage<unsigned char> & I,const vpHomogeneousMatrix & cMo,const vpCameraParameters & camera,const vpColor & col,unsigned int thickness,const bool)575 void vpMbtDistanceKltCylinder::display(const vpImage<unsigned char> &I, const vpHomogeneousMatrix &cMo,
576 const vpCameraParameters &camera, const vpColor &col,
577 unsigned int thickness, const bool /*displayFullModel*/)
578 {
579 std::vector<std::vector<double> > models = getModelForDisplay(cMo, camera);
580
581 for (size_t i = 0; i < models.size(); i++) {
582 vpImagePoint ip1(models[i][1], models[i][2]);
583 vpImagePoint ip2(models[i][3], models[i][4]);
584 vpDisplay::displayLine(I, ip1, ip2, col, thickness);
585 }
586 }
587
display(const vpImage<vpRGBa> & I,const vpHomogeneousMatrix & cMo,const vpCameraParameters & camera,const vpColor & col,unsigned int thickness,const bool)588 void vpMbtDistanceKltCylinder::display(const vpImage<vpRGBa> &I, const vpHomogeneousMatrix &cMo,
589 const vpCameraParameters &camera, const vpColor &col,
590 unsigned int thickness, const bool /*displayFullModel*/)
591 {
592 std::vector<std::vector<double> > models = getModelForDisplay(cMo, camera);
593
594 for (size_t i = 0; i < models.size(); i++) {
595 vpImagePoint ip1(models[i][1], models[i][2]);
596 vpImagePoint ip2(models[i][3], models[i][4]);
597
598 vpDisplay::displayLine(I, ip1, ip2, col, thickness);
599 }
600 }
601
602 /*!
603 Return a list of features parameters for display.
604 - Parameters are: `<feature id (here 1 for KLT)>`, `<pt.i()>`, `<pt.j()>`,
605 `<klt_id.i()>`, `<klt_id.j()>`, `<klt_id.id>`
606 */
getFeaturesForDisplay()607 std::vector<std::vector<double> > vpMbtDistanceKltCylinder::getFeaturesForDisplay()
608 {
609 std::vector<std::vector<double> > features;
610
611 std::map<int, vpImagePoint>::const_iterator iter = curPoints.begin();
612 for (; iter != curPoints.end(); ++iter) {
613 int id(iter->first);
614 vpImagePoint iP;
615 iP.set_i(static_cast<double>(iter->second.get_i()));
616 iP.set_j(static_cast<double>(iter->second.get_j()));
617
618 vpImagePoint iP2;
619 iP2.set_i(vpMath::round(iP.get_i() + 7));
620 iP2.set_j(vpMath::round(iP.get_j() + 7));
621
622 #if (VISP_CXX_STANDARD >= VISP_CXX_STANDARD_11)
623 std::vector<double> params = {1, //KLT
624 iP.get_i(),
625 iP.get_j(),
626 iP2.get_i(),
627 iP2.get_j(),
628 static_cast<double>(id)};
629 #else
630 std::vector<double> params;
631 params.push_back(1); //KLT
632 params.push_back(iP.get_i());
633 params.push_back(iP.get_j());
634 params.push_back(iP2.get_i());
635 params.push_back(iP2.get_j());
636 params.push_back(static_cast<double>(id));
637 #endif
638 features.push_back(params);
639 }
640
641 return features;
642 }
643
644 /*!
645 Return a list of line parameters to display the primitive at a given pose and camera parameters.
646 Parameters are: <primitive id (here 0 for line)>, <pt_start.i()>, <pt_start.j()>
647 <pt_end.i()>, <pt_end.j()>
648
649 \param cMo : Pose used to project the 3D model into the image.
650 \param camera : The camera parameters.
651 */
getModelForDisplay(const vpHomogeneousMatrix & cMo,const vpCameraParameters & camera)652 std::vector<std::vector<double> > vpMbtDistanceKltCylinder::getModelForDisplay(const vpHomogeneousMatrix &cMo,
653 const vpCameraParameters &camera)
654 {
655 std::vector<std::vector<double> > models;
656
657 // if(isvisible || displayFullModel)
658 {
659 // Perspective projection
660 circle1.changeFrame(cMo);
661 circle2.changeFrame(cMo);
662 cylinder.changeFrame(cMo);
663
664 try {
665 circle1.projection();
666 } catch (...) {
667 std::cout << "Problem projection circle 1";
668 }
669 try {
670 circle2.projection();
671 } catch (...) {
672 std::cout << "Problem projection circle 2";
673 }
674
675 cylinder.projection();
676
677 double rho1, theta1;
678 double rho2, theta2;
679
680 // Meters to pixels conversion
681 vpMeterPixelConversion::convertLine(camera, cylinder.getRho1(), cylinder.getTheta1(), rho1, theta1);
682 vpMeterPixelConversion::convertLine(camera, cylinder.getRho2(), cylinder.getTheta2(), rho2, theta2);
683
684 // Determine intersections between circles and limbos
685 double i11, i12, i21, i22, j11, j12, j21, j22;
686
687 vpCircle::computeIntersectionPoint(circle1, camera, rho1, theta1, i11, j11);
688 vpCircle::computeIntersectionPoint(circle2, camera, rho1, theta1, i12, j12);
689
690 vpCircle::computeIntersectionPoint(circle1, camera, rho2, theta2, i21, j21);
691 vpCircle::computeIntersectionPoint(circle2, camera, rho2, theta2, i22, j22);
692
693 // Create the image points
694 vpImagePoint ip11, ip12, ip21, ip22;
695 ip11.set_ij(i11, j11);
696 ip12.set_ij(i12, j12);
697 ip21.set_ij(i21, j21);
698 ip22.set_ij(i22, j22);
699
700 #if (VISP_CXX_STANDARD >= VISP_CXX_STANDARD_11)
701 std::vector<double> params1 = {0, //line parameters
702 ip11.get_i(),
703 ip11.get_j(),
704 ip12.get_i(),
705 ip12.get_j()};
706 models.push_back(params1);
707
708 std::vector<double> params2 = {0, //line parameters
709 ip21.get_i(),
710 ip21.get_j(),
711 ip22.get_i(),
712 ip22.get_j()};
713 #else
714 std::vector<double> params1, params2;
715 params1.push_back(0); //line parameters
716 params1.push_back(ip11.get_i());
717 params1.push_back(ip11.get_j());
718 params1.push_back(ip12.get_i());
719 params1.push_back(ip12.get_j());
720
721 params2.push_back(0); //line parameters
722 params2.push_back(ip21.get_i());
723 params2.push_back(ip21.get_j());
724 params2.push_back(ip22.get_i());
725 params2.push_back(ip22.get_j());
726 #endif
727 models.push_back(params1);
728 models.push_back(params2);
729 }
730
731 return models;
732 }
733
734 // ######################
735 // Private Functions
736 // ######################
737
computeZ(const double & x,const double & y)738 double vpMbtDistanceKltCylinder::computeZ(const double &x, const double &y)
739 {
740 // double A = x*x + y*y + 1 -
741 // ((cylinder.getA()*x+cylinder.getB()*y+cylinder.getC()) *
742 // (cylinder.getA()*x+cylinder.getB()*y+cylinder.getC())); double B = (x *
743 // cylinder.getX() + y * cylinder.getY() + cylinder.getZ()); double C =
744 // cylinder.getX() * cylinder.getX() + cylinder.getY() * cylinder.getY() +
745 // cylinder.getZ() * cylinder.getZ() - cylinder.getR() * cylinder.getR();
746 //
747 // return (B - std::sqrt(B*B - A*C))/A;
748
749 return cylinder.computeZ(x, y);
750 }
751 #elif !defined(VISP_BUILD_SHARED_LIBS)
752 // Work arround to avoid warning:
753 // libvisp_mbt.a(vpMbtDistanceKltCylinder.cpp.o) has no symbols
dummy_vpMbtDistanceKltCylinder()754 void dummy_vpMbtDistanceKltCylinder(){};
755 #endif
756