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 * Visual feature circle.
33 *
34 * Authors:
35 * Eric Marchand
36 *
37 *****************************************************************************/
38
39 #include <visp3/core/vpCircle.h>
40
41 #include <visp3/core/vpFeatureDisplay.h>
42
init()43 void vpCircle::init()
44 {
45 oP.resize(7);
46 cP.resize(7);
47
48 p.resize(5);
49 }
50
51 /*!
52 Set the parameters of the 3D circle in the object frame.
53
54 \param oP_ : This 7-dim vector defines the parameters oP[0], oP[1], oP[2] corresponding
55 to parameters oA, oB, oC of the plane with equation oA*(x-oX)+oB*(y-oY)+oC*(z-oZ)=0
56 passing through the 3D sphere center. oP[3], oP[4], oP[5] correspond to oX, oY, oZ the
57 coordinates of the center of the sphere. oP[6] corresponds to the radius of
58 the sphere.
59 */
setWorldCoordinates(const vpColVector & oP_)60 void vpCircle::setWorldCoordinates(const vpColVector &oP_) { this->oP = oP_; }
61
62 /*!
63 Set the 3D circle coordinates in the object frame.
64
65 \param oA, oB, oC : Parameters of the plane with equation oA*(x-oX)+oB*(y-oY)+oC*(z-oZ)=0
66 passing through the 3D sphere center.
67 \param oX : Coordinate of the center of the sphere along X-axis in the object frame.
68 \param oY : Coordinate of the center of the sphere along Y-axis in the object frame.
69 \param oZ : Coordinate of the center of the sphere along Z-axis in the object frame.
70 \param R : Radius of the sphere.
71 */
setWorldCoordinates(double oA,double oB,double oC,double oX,double oY,double oZ,double R)72 void vpCircle::setWorldCoordinates(double oA, double oB, double oC, double oX, double oY, double oZ, double R)
73 {
74 oP[0] = oA;
75 oP[1] = oB;
76 oP[2] = oC;
77 oP[3] = oX;
78 oP[4] = oY;
79 oP[5] = oZ;
80 oP[6] = R;
81 }
82
83 /*!
84 * Default constructor that initialize internal vectors.
85 */
vpCircle()86 vpCircle::vpCircle() { init(); }
87
88 /*!
89 Construct the circle from the intersection of a plane and a sphere.
90
91 \param oP_ : This 7-dim vector defines the parameters oP[0], oP[1], oP[2] corresponding
92 to parameters oA, oB, oC of the plane with equation oA*(x-oX)+oB*(y-oY)+oC*(z-oZ)=0
93 passing through the 3D sphere center. oP[3], oP[4], oP[5] correspond to oX, oY, oZ the
94 coordinates of the center of the sphere. oP[6] corresponds to the radius of
95 the sphere.
96
97 \sa setWorldCoordinates()
98 */
vpCircle(const vpColVector & oP_)99 vpCircle::vpCircle(const vpColVector &oP_)
100 {
101 init();
102 setWorldCoordinates(oP_);
103 }
104
105 /*!
106 Construct the 3D circle from the intersection of a plane and a sphere with
107 coordinates expressed in the object frame.
108
109 \param oA, oB, oC : Parameters of the plane with equation oA*(x-oX)+oB*(y-oY)+oC*(z-oZ)=0
110 passing through the 3D sphere center.
111 \param oX : Coordinate of the center of the sphere along X-axis in the object frame.
112 \param oY : Coordinate of the center of the sphere along Y-axis in the object frame.
113 \param oZ : Coordinate of the center of the sphere along Z-axis in the object frame.
114 \param R : Radius of the sphere.
115
116 \sa setWorldCoordinates()
117 */
vpCircle(double oA,double oB,double oC,double oX,double oY,double oZ,double R)118 vpCircle::vpCircle(double oA, double oB, double oC, double oX, double oY, double oZ, double R)
119 {
120 init();
121 setWorldCoordinates(oA, oB, oC, oX, oY, oZ, R);
122 }
123
124 /*!
125 * Default destructor that does nothing.
126 */
~vpCircle()127 vpCircle::~vpCircle() {}
128
129 /*!
130 Perspective projection of the circle.
131
132 From the 3D parameters of the circle in the camera frame available in cP,
133 computes the 2D parameters of the ellipse resulting from the perspective
134 projection in the image plane. Those 2D parameters are available in p
135 vector.
136
137 See vpCircle::projection(const vpColVector &, vpColVector &) const for a more
138 detailed description of the parameters.
139 */
projection()140 void vpCircle::projection() { projection(cP, p); }
141
142 /*!
143 Perspective projection of the circle.
144 \param cP_: 3D cercle input parameters expressed in the camera frame.
145 This 7-dim vector contains the following parameters: cA, cB, cC, cX, cY, cZ, R where
146 - cA, cB, cC are the parameters of the plane with equation cA*(x-cX)+cB*(y-cY)+cC*(z-cZ)=0
147 passing through the 3D sphere center.
148 - cX, cY, cZ are the 3D coordinates of the circle in the camera frame
149 - R is the circle radius in [m].
150
151 \param p_: 2D circle output parameters. This is a 5 dimension vector. It
152 contains the following parameters: x, y, n20, n11, n02 where:
153 - x, y are the normalized coordinates of the ellipse centroid (ie the
154 perspective projection of a 3D circle becomes a 2D ellipse in the image) in
155 the image plane.
156 - n20, n11, n02 which are the second order centered moments of
157 the ellipse normalized by its area (i.e., such that \f$n_{ij} = \mu_{ij}/a\f$ where
158 \f$\mu_{ij}\f$ are the centered moments and a the area).
159 */
projection(const vpColVector & cP_,vpColVector & p_) const160 void vpCircle::projection(const vpColVector &cP_, vpColVector &p_) const
161 {
162 p_.resize(5, false);
163
164 vpColVector K(6);
165 {
166 double A = cP_[0];
167 double B = cP_[1];
168 double C = cP_[2];
169
170 double X0 = cP_[3];
171 double Y0 = cP_[4];
172 double Z0 = cP_[5];
173
174 double r = cP_[6];
175
176 // projection
177 double s = X0 * X0 + Y0 * Y0 + Z0 * Z0 - r * r;
178 double det = A * X0 + B * Y0 + C * Z0;
179 A = A / det;
180 B = B / det;
181 C = C / det;
182
183 K[0] = 1 - 2 * A * X0 + A * A * s;
184 K[1] = 1 - 2 * B * Y0 + B * B * s;
185 K[2] = -A * Y0 - B * X0 + A * B * s;
186 K[3] = -C * X0 - A * Z0 + A * C * s;
187 K[4] = -C * Y0 - B * Z0 + B * C * s;
188 K[5] = 1 - 2 * C * Z0 + C * C * s;
189 }
190
191 double det = K[2] * K[2] - K[0] * K[1];
192 if (fabs(det) < 1e-8) {
193 throw(vpException(vpException::divideByZeroError, "division par 0"));
194 }
195
196 double xc = (K[1] * K[3] - K[2] * K[4]) / det;
197 double yc = (K[0] * K[4] - K[2] * K[3]) / det;
198
199 double c = sqrt((K[0] - K[1]) * (K[0] - K[1]) + 4 * K[2] * K[2]);
200 double s = 2 * (K[0] * xc * xc + 2 * K[2] * xc * yc + K[1] * yc * yc - K[5]);
201
202 double A, B, E;
203
204 if (fabs(K[2]) < std::numeric_limits<double>::epsilon()) {
205 E = 0.0;
206 if (K[0] > K[1]) {
207 A = sqrt(s / (K[0] + K[1] + c));
208 B = sqrt(s / (K[0] + K[1] - c));
209 } else {
210 A = sqrt(s / (K[0] + K[1] - c));
211 B = sqrt(s / (K[0] + K[1] + c));
212 }
213 } else {
214 E = (K[1] - K[0] + c) / (2 * K[2]);
215 if (fabs(E) > 1.0) {
216 A = sqrt(s / (K[0] + K[1] + c));
217 B = sqrt(s / (K[0] + K[1] - c));
218 } else {
219 A = sqrt(s / (K[0] + K[1] - c));
220 B = sqrt(s / (K[0] + K[1] + c));
221 E = -1.0 / E;
222 }
223 }
224
225 // Chaumette PhD Thesis 1990, eq 2.72 divided by 4 since n_ij = mu_ij_chaumette_thesis / 4
226 det = 4 * (1.0 + vpMath::sqr(E));
227 double n20 = (vpMath::sqr(A) + vpMath::sqr(B * E)) / det;
228 double n11 = (vpMath::sqr(A) - vpMath::sqr(B)) * E / det;
229 double n02 = (vpMath::sqr(B) + vpMath::sqr(A * E)) / det;
230
231 p_[0] = xc;
232 p_[1] = yc;
233 p_[2] = n20;
234 p_[3] = n11;
235 p_[4] = n02;
236 }
237
238 /*!
239 From the 3D coordinates of the circle in the object frame oP that are set using for
240 example vpCircle(double oA, double oB, double oC, double oX, double oY, double oZ, double R)
241 or setWorldCoordinates(), compute the 3D coordinates of the circle in the camera frame cP = cMo * oP.
242
243 \param cMo : Transformation from camera to object frame.
244 \param cP_ : 3D normalized coordinates of the circle in the camera frame cP = (cA, cB, cC, cX, cY, cZ, R).
changeFrame(const vpHomogeneousMatrix & cMo,vpColVector & cP_) const245 */void vpCircle::changeFrame(const vpHomogeneousMatrix &cMo, vpColVector &cP_) const
246 {
247 cP_.resize(7, false);
248
249 double A, B, C;
250 A = cMo[0][0] * oP[0] + cMo[0][1] * oP[1] + cMo[0][2] * oP[2];
251 B = cMo[1][0] * oP[0] + cMo[1][1] * oP[1] + cMo[1][2] * oP[2];
252 C = cMo[2][0] * oP[0] + cMo[2][1] * oP[1] + cMo[2][2] * oP[2];
253
254 double X0, Y0, Z0;
255 X0 = cMo[0][3] + cMo[0][0] * oP[3] + cMo[0][1] * oP[4] + cMo[0][2] * oP[5];
256 Y0 = cMo[1][3] + cMo[1][0] * oP[3] + cMo[1][1] * oP[4] + cMo[1][2] * oP[5];
257 Z0 = cMo[2][3] + cMo[2][0] * oP[3] + cMo[2][1] * oP[4] + cMo[2][2] * oP[5];
258 double R = oP[6];
259
260 cP_[0] = A;
261 cP_[1] = B;
262 cP_[2] = C;
263
264 cP_[3] = X0;
265 cP_[4] = Y0;
266 cP_[5] = Z0;
267
268 cP_[6] = R;
269 }
270
271 /*!
272 * Perspective projection of the circle.
273 * Internal circle parameters are modified in cP.
274 *
275 * \param cMo : Homogeneous transformation from camera frame to object frame.
276 */
changeFrame(const vpHomogeneousMatrix & cMo)277 void vpCircle::changeFrame(const vpHomogeneousMatrix &cMo)
278 {
279 double A, B, C;
280 A = cMo[0][0] * oP[0] + cMo[0][1] * oP[1] + cMo[0][2] * oP[2];
281 B = cMo[1][0] * oP[0] + cMo[1][1] * oP[1] + cMo[1][2] * oP[2];
282 C = cMo[2][0] * oP[0] + cMo[2][1] * oP[1] + cMo[2][2] * oP[2];
283
284 double X0, Y0, Z0;
285 X0 = cMo[0][3] + cMo[0][0] * oP[3] + cMo[0][1] * oP[4] + cMo[0][2] * oP[5];
286 Y0 = cMo[1][3] + cMo[1][0] * oP[3] + cMo[1][1] * oP[4] + cMo[1][2] * oP[5];
287 Z0 = cMo[2][3] + cMo[2][0] * oP[3] + cMo[2][1] * oP[4] + cMo[2][2] * oP[5];
288 double R = oP[6];
289
290 cP[0] = A;
291 cP[1] = B;
292 cP[2] = C;
293
294 cP[3] = X0;
295 cP[4] = Y0;
296 cP[5] = Z0;
297
298 cP[6] = R;
299 }
300
301 /*!
302 * Display the projection of a 3D circle in image \e I.
303 *
304 * \param I : Image used as background.
305 * \param cam : Camera parameters.
306 * \param color : Color used to draw the circle.
307 * \param thickness : Thickness used to draw the circle.
308 */
display(const vpImage<unsigned char> & I,const vpCameraParameters & cam,const vpColor & color,unsigned int thickness)309 void vpCircle::display(const vpImage<unsigned char> &I, const vpCameraParameters &cam, const vpColor &color,
310 unsigned int thickness)
311 {
312 vpFeatureDisplay::displayEllipse(p[0], p[1], p[2], p[3], p[4], cam, I, color, thickness);
313 }
314
315 /*!
316 * Display the projection of a sphere in image \e I.
317 * This method is non destructive wrt. cP and p internal circle parameters.
318 *
319 * \param I : Image used as background.
320 * \param cMo : Homogeneous transformation from camera frame to object frame.
321 * The circle is considered as viewed from this camera position.
322 * \param cam : Camera parameters.
323 * \param color : Color used to draw the sphere.
324 * \param thickness : Thickness used to draw the circle.
325 */
display(const vpImage<unsigned char> & I,const vpHomogeneousMatrix & cMo,const vpCameraParameters & cam,const vpColor & color,unsigned int thickness)326 void vpCircle::display(const vpImage<unsigned char> &I, const vpHomogeneousMatrix &cMo, const vpCameraParameters &cam,
327 const vpColor &color, unsigned int thickness)
328 {
329 vpColVector _cP, _p;
330 changeFrame(cMo, _cP);
331 projection(_cP, _p);
332 vpFeatureDisplay::displayEllipse(_p[0], _p[1], _p[2], _p[3], _p[4], cam, I, color, thickness);
333 }
334 //! for memory issue (used by the vpServo class only)
duplicate() const335 vpCircle *vpCircle::duplicate() const
336 {
337 vpCircle *feature = new vpCircle(*this);
338 return feature;
339 }
340
341 /*!
342 Computes the coordinates of the point corresponding to the intersection
343 between a circle and a line.
344
345 \warning This functions assumes changeFrame() and projection() have already
346 been called.
347
348 \sa changeFrame(), projection()
349
350 \param circle : Circle to consider for the intersection.
351 \param cam : Camera parameters that have to be used for the intersection computation.
352 \param rho : The rho parameter of the line.
353 \param theta : The theta parameter of the line.
354 \param i : resulting i-coordinate of the intersection point.
355 \param j : resulting j-coordinate of the intersection
356 point.
357 */
computeIntersectionPoint(const vpCircle & circle,const vpCameraParameters & cam,const double & rho,const double & theta,double & i,double & j)358 void vpCircle::computeIntersectionPoint(const vpCircle &circle, const vpCameraParameters &cam, const double &rho,
359 const double &theta, double &i, double &j)
360 {
361 // This was taken from the code of art-v1. (from the artCylinder class)
362 double px = cam.get_px();
363 double py = cam.get_py();
364 double u0 = cam.get_u0();
365 double v0 = cam.get_v0();
366
367 double n11 = circle.p[3];
368 double n02 = circle.p[4];
369 double n20 = circle.p[2];
370 double Xg = u0 + circle.p[0] * px;
371 double Yg = v0 + circle.p[1] * py;
372
373 // Find Intersection between line and ellipse in the image.
374
375 // Optimised calculation for X
376 double stheta = sin(theta);
377 double ctheta = cos(theta);
378 double sctheta = stheta * ctheta;
379 double m11yg = n11 * Yg;
380 double ctheta2 = vpMath::sqr(ctheta);
381 double m02xg = n02 * Xg;
382 double m11stheta = n11 * stheta;
383 j = ((n11 * Xg * sctheta - n20 * Yg * sctheta + n20 * rho * ctheta - m11yg + m11yg * ctheta2 + m02xg -
384 m02xg * ctheta2 + m11stheta * rho) /
385 (n20 * ctheta2 + 2.0 * m11stheta * ctheta + n02 - n02 * ctheta2));
386 // Optimised calculation for Y
387 double rhom02 = rho * n02;
388 double sctheta2 = stheta * ctheta2;
389 double ctheta3 = ctheta2 * ctheta;
390 i = (-(-rho * n11 * stheta * ctheta - rhom02 + rhom02 * ctheta2 + n11 * Xg * sctheta2 - n20 * Yg * sctheta2 -
391 ctheta * n11 * Yg + ctheta3 * n11 * Yg + ctheta * n02 * Xg - ctheta3 * n02 * Xg) /
392 (n20 * ctheta2 + 2.0 * n11 * stheta * ctheta + n02 - n02 * ctheta2) / stheta);
393 }
394