1 | /*M/////////////////////////////////////////////////////////////////////////////////////// |
2 | // |
3 | // IMPORTANT: READ BEFORE DOWNLOADING, COPYING, INSTALLING OR USING. |
4 | // |
5 | // By downloading, copying, installing or using the software you agree to this license. |
6 | // If you do not agree to this license, do not download, install, |
7 | // copy or use the software. |
8 | // |
9 | // |
10 | // Intel License Agreement |
11 | // For Open Source Computer Vision Library |
12 | // |
13 | // Copyright (C) 2000, Intel Corporation, all rights reserved. |
14 | // Third party copyrights are property of their respective owners. |
15 | // |
16 | // Redistribution and use in source and binary forms, with or without modification, |
17 | // are permitted provided that the following conditions are met: |
18 | // |
19 | // * Redistribution's of source code must retain the above copyright notice, |
20 | // this list of conditions and the following disclaimer. |
21 | // |
22 | // * Redistribution's in binary form must reproduce the above copyright notice, |
23 | // this list of conditions and the following disclaimer in the documentation |
24 | // and/or other materials provided with the distribution. |
25 | // |
26 | // * The name of Intel Corporation may not be used to endorse or promote products |
27 | // derived from this software without specific prior written permission. |
28 | // |
29 | // This software is provided by the copyright holders and contributors "as is" and |
30 | // any express or implied warranties, including, but not limited to, the implied |
31 | // warranties of merchantability and fitness for a particular purpose are disclaimed. |
32 | // In no event shall the Intel Corporation or contributors be liable for any direct, |
33 | // indirect, incidental, special, exemplary, or consequential damages |
34 | // (including, but not limited to, procurement of substitute goods or services; |
35 | // loss of use, data, or profits; or business interruption) however caused |
36 | // and on any theory of liability, whether in contract, strict liability, |
37 | // or tort (including negligence or otherwise) arising in any way out of |
38 | // the use of this software, even if advised of the possibility of such damage. |
39 | // |
40 | //M*/ |
41 | #include "precomp.hpp" |
42 | #include "opencv2/core/hal/intrin.hpp" |
43 | |
44 | namespace cv |
45 | { |
46 | |
47 | const float EPS = 1.0e-4f; |
48 | |
49 | static void findCircle3pts(Point2f *pts, Point2f ¢er, float &radius) |
50 | { |
51 | // two edges of the triangle v1, v2 |
52 | Point2f v1 = pts[1] - pts[0]; |
53 | Point2f v2 = pts[2] - pts[0]; |
54 | |
55 | // center is intersection of midperpendicular lines of the two edges v1, v2 |
56 | // a1*x + b1*y = c1 where a1 = v1.x, b1 = v1.y |
57 | // a2*x + b2*y = c2 where a2 = v2.x, b2 = v2.y |
58 | Point2f midPoint1 = (pts[0] + pts[1]) / 2.0f; |
59 | float c1 = midPoint1.x * v1.x + midPoint1.y * v1.y; |
60 | Point2f midPoint2 = (pts[0] + pts[2]) / 2.0f; |
61 | float c2 = midPoint2.x * v2.x + midPoint2.y * v2.y; |
62 | float det = v1.x * v2.y - v1.y * v2.x; |
63 | if (fabs(x: det) <= EPS) |
64 | { |
65 | // v1 and v2 are colinear, so the longest distance between any 2 points |
66 | // is the diameter of the minimum enclosing circle. |
67 | float d1 = normL2Sqr<float>(pt: pts[0] - pts[1]); |
68 | float d2 = normL2Sqr<float>(pt: pts[0] - pts[2]); |
69 | float d3 = normL2Sqr<float>(pt: pts[1] - pts[2]); |
70 | radius = sqrt(x: std::max(a: d1, b: std::max(a: d2, b: d3))) * 0.5f + EPS; |
71 | if (d1 >= d2 && d1 >= d3) |
72 | { |
73 | center = (pts[0] + pts[1]) * 0.5f; |
74 | } |
75 | else if (d2 >= d1 && d2 >= d3) |
76 | { |
77 | center = (pts[0] + pts[2]) * 0.5f; |
78 | } |
79 | else |
80 | { |
81 | CV_DbgAssert(d3 >= d1 && d3 >= d2); |
82 | center = (pts[1] + pts[2]) * 0.5f; |
83 | } |
84 | return; |
85 | } |
86 | float cx = (c1 * v2.y - c2 * v1.y) / det; |
87 | float cy = (v1.x * c2 - v2.x * c1) / det; |
88 | center.x = (float)cx; |
89 | center.y = (float)cy; |
90 | cx -= pts[0].x; |
91 | cy -= pts[0].y; |
92 | radius = (float)(std::sqrt(x: cx *cx + cy * cy)) + EPS; |
93 | } |
94 | |
95 | template<typename PT> |
96 | static void findThirdPoint(const PT *pts, int i, int j, Point2f ¢er, float &radius) |
97 | { |
98 | center.x = (float)(pts[j].x + pts[i].x) / 2.0f; |
99 | center.y = (float)(pts[j].y + pts[i].y) / 2.0f; |
100 | float dx = (float)(pts[j].x - pts[i].x); |
101 | float dy = (float)(pts[j].y - pts[i].y); |
102 | radius = (float)norm(pt: Point2f(dx, dy)) / 2.0f + EPS; |
103 | |
104 | for (int k = 0; k < j; ++k) |
105 | { |
106 | dx = center.x - (float)pts[k].x; |
107 | dy = center.y - (float)pts[k].y; |
108 | if (norm(pt: Point2f(dx, dy)) < radius) |
109 | { |
110 | continue; |
111 | } |
112 | else |
113 | { |
114 | Point2f ptsf[3]; |
115 | ptsf[0] = (Point2f)pts[i]; |
116 | ptsf[1] = (Point2f)pts[j]; |
117 | ptsf[2] = (Point2f)pts[k]; |
118 | Point2f new_center; float new_radius = 0; |
119 | findCircle3pts(pts: ptsf, center&: new_center, radius&: new_radius); |
120 | if (new_radius > 0) |
121 | { |
122 | radius = new_radius; |
123 | center = new_center; |
124 | } |
125 | } |
126 | } |
127 | } |
128 | |
129 | |
130 | template<typename PT> |
131 | void findSecondPoint(const PT *pts, int i, Point2f ¢er, float &radius) |
132 | { |
133 | center.x = (float)(pts[0].x + pts[i].x) / 2.0f; |
134 | center.y = (float)(pts[0].y + pts[i].y) / 2.0f; |
135 | float dx = (float)(pts[0].x - pts[i].x); |
136 | float dy = (float)(pts[0].y - pts[i].y); |
137 | radius = (float)norm(pt: Point2f(dx, dy)) / 2.0f + EPS; |
138 | |
139 | for (int j = 1; j < i; ++j) |
140 | { |
141 | dx = center.x - (float)pts[j].x; |
142 | dy = center.y - (float)pts[j].y; |
143 | if (norm(pt: Point2f(dx, dy)) < radius) |
144 | { |
145 | continue; |
146 | } |
147 | else |
148 | { |
149 | Point2f new_center; float new_radius = 0; |
150 | findThirdPoint(pts, i, j, new_center, new_radius); |
151 | if (new_radius > 0) |
152 | { |
153 | radius = new_radius; |
154 | center = new_center; |
155 | } |
156 | } |
157 | } |
158 | } |
159 | |
160 | |
161 | template<typename PT> |
162 | static void findMinEnclosingCircle(const PT *pts, int count, Point2f ¢er, float &radius) |
163 | { |
164 | center.x = (float)(pts[0].x + pts[1].x) / 2.0f; |
165 | center.y = (float)(pts[0].y + pts[1].y) / 2.0f; |
166 | float dx = (float)(pts[0].x - pts[1].x); |
167 | float dy = (float)(pts[0].y - pts[1].y); |
168 | radius = (float)norm(pt: Point2f(dx, dy)) / 2.0f + EPS; |
169 | |
170 | for (int i = 2; i < count; ++i) |
171 | { |
172 | dx = (float)pts[i].x - center.x; |
173 | dy = (float)pts[i].y - center.y; |
174 | float d = (float)norm(pt: Point2f(dx, dy)); |
175 | if (d < radius) |
176 | { |
177 | continue; |
178 | } |
179 | else |
180 | { |
181 | Point2f new_center; float new_radius = 0; |
182 | findSecondPoint(pts, i, new_center, new_radius); |
183 | if (new_radius > 0) |
184 | { |
185 | radius = new_radius; |
186 | center = new_center; |
187 | } |
188 | } |
189 | } |
190 | } |
191 | } // namespace cv |
192 | |
193 | // see Welzl, Emo. Smallest enclosing disks (balls and ellipsoids). Springer Berlin Heidelberg, 1991. |
194 | void cv::minEnclosingCircle( InputArray _points, Point2f& _center, float& _radius ) |
195 | { |
196 | CV_INSTRUMENT_REGION(); |
197 | |
198 | Mat points = _points.getMat(); |
199 | int count = points.checkVector(elemChannels: 2); |
200 | int depth = points.depth(); |
201 | CV_Assert(count >= 0 && (depth == CV_32F || depth == CV_32S)); |
202 | |
203 | _center.x = _center.y = 0.f; |
204 | _radius = 0.f; |
205 | |
206 | if( count == 0 ) |
207 | return; |
208 | |
209 | bool is_float = depth == CV_32F; |
210 | const Point* ptsi = points.ptr<Point>(); |
211 | const Point2f* ptsf = points.ptr<Point2f>(); |
212 | |
213 | switch (count) |
214 | { |
215 | case 1: |
216 | { |
217 | _center = (is_float) ? ptsf[0] : Point2f((float)ptsi[0].x, (float)ptsi[0].y); |
218 | _radius = EPS; |
219 | break; |
220 | } |
221 | case 2: |
222 | { |
223 | Point2f p1 = (is_float) ? ptsf[0] : Point2f((float)ptsi[0].x, (float)ptsi[0].y); |
224 | Point2f p2 = (is_float) ? ptsf[1] : Point2f((float)ptsi[1].x, (float)ptsi[1].y); |
225 | _center.x = (p1.x + p2.x) / 2.0f; |
226 | _center.y = (p1.y + p2.y) / 2.0f; |
227 | _radius = (float)(norm(pt: p1 - p2) / 2.0) + EPS; |
228 | break; |
229 | } |
230 | default: |
231 | { |
232 | Point2f center; |
233 | float radius = 0.f; |
234 | if (is_float) |
235 | { |
236 | findMinEnclosingCircle<Point2f>(pts: ptsf, count, center, radius); |
237 | #if 0 |
238 | for (size_t m = 0; m < count; ++m) |
239 | { |
240 | float d = (float)norm(ptsf[m] - center); |
241 | if (d > radius) |
242 | { |
243 | printf("error!\n" ); |
244 | } |
245 | } |
246 | #endif |
247 | } |
248 | else |
249 | { |
250 | findMinEnclosingCircle<Point>(pts: ptsi, count, center, radius); |
251 | #if 0 |
252 | for (size_t m = 0; m < count; ++m) |
253 | { |
254 | double dx = ptsi[m].x - center.x; |
255 | double dy = ptsi[m].y - center.y; |
256 | double d = std::sqrt(dx * dx + dy * dy); |
257 | if (d > radius) |
258 | { |
259 | printf("error!\n" ); |
260 | } |
261 | } |
262 | #endif |
263 | } |
264 | _center = center; |
265 | _radius = radius; |
266 | break; |
267 | } |
268 | } |
269 | } |
270 | |
271 | |
272 | // calculates length of a curve (e.g. contour perimeter) |
273 | double cv::arcLength( InputArray _curve, bool is_closed ) |
274 | { |
275 | CV_INSTRUMENT_REGION(); |
276 | |
277 | Mat curve = _curve.getMat(); |
278 | int count = curve.checkVector(elemChannels: 2); |
279 | int depth = curve.depth(); |
280 | CV_Assert( count >= 0 && (depth == CV_32F || depth == CV_32S)); |
281 | double perimeter = 0; |
282 | |
283 | int i; |
284 | |
285 | if( count <= 1 ) |
286 | return 0.; |
287 | |
288 | bool is_float = depth == CV_32F; |
289 | int last = is_closed ? count-1 : 0; |
290 | const Point* pti = curve.ptr<Point>(); |
291 | const Point2f* ptf = curve.ptr<Point2f>(); |
292 | |
293 | Point2f prev = is_float ? ptf[last] : Point2f((float)pti[last].x,(float)pti[last].y); |
294 | |
295 | for( i = 0; i < count; i++ ) |
296 | { |
297 | Point2f p = is_float ? ptf[i] : Point2f((float)pti[i].x,(float)pti[i].y); |
298 | float dx = p.x - prev.x, dy = p.y - prev.y; |
299 | perimeter += std::sqrt(x: dx*dx + dy*dy); |
300 | |
301 | prev = p; |
302 | } |
303 | |
304 | return perimeter; |
305 | } |
306 | |
307 | // area of a whole sequence |
308 | double cv::contourArea( InputArray _contour, bool oriented ) |
309 | { |
310 | CV_INSTRUMENT_REGION(); |
311 | |
312 | Mat contour = _contour.getMat(); |
313 | int npoints = contour.checkVector(elemChannels: 2); |
314 | int depth = contour.depth(); |
315 | CV_Assert(npoints >= 0 && (depth == CV_32F || depth == CV_32S)); |
316 | |
317 | if( npoints == 0 ) |
318 | return 0.; |
319 | |
320 | double a00 = 0; |
321 | bool is_float = depth == CV_32F; |
322 | const Point* ptsi = contour.ptr<Point>(); |
323 | const Point2f* ptsf = contour.ptr<Point2f>(); |
324 | Point2f prev = is_float ? ptsf[npoints-1] : Point2f((float)ptsi[npoints-1].x, (float)ptsi[npoints-1].y); |
325 | |
326 | for( int i = 0; i < npoints; i++ ) |
327 | { |
328 | Point2f p = is_float ? ptsf[i] : Point2f((float)ptsi[i].x, (float)ptsi[i].y); |
329 | a00 += (double)prev.x * p.y - (double)prev.y * p.x; |
330 | prev = p; |
331 | } |
332 | |
333 | a00 *= 0.5; |
334 | if( !oriented ) |
335 | a00 = fabs(x: a00); |
336 | |
337 | return a00; |
338 | } |
339 | |
340 | namespace cv |
341 | { |
342 | |
343 | static inline Point2f getOfs(int i, float eps) |
344 | { |
345 | return Point2f(((i & 1)*2 - 1)*eps, ((i & 2) - 1)*eps); |
346 | } |
347 | |
348 | static RotatedRect fitEllipseNoDirect( InputArray _points ) |
349 | { |
350 | CV_INSTRUMENT_REGION(); |
351 | |
352 | Mat points = _points.getMat(); |
353 | int i, n = points.checkVector(elemChannels: 2); |
354 | int depth = points.depth(); |
355 | CV_Assert( n >= 0 && (depth == CV_32F || depth == CV_32S)); |
356 | |
357 | RotatedRect box; |
358 | |
359 | if( n < 5 ) |
360 | CV_Error( cv::Error::StsBadSize, "There should be at least 5 points to fit the ellipse" ); |
361 | |
362 | // New fitellipse algorithm, contributed by Dr. Daniel Weiss |
363 | Point2f c(0,0); |
364 | double gfp[5] = {0}, rp[5] = {0}, t, vd[25]={0}, wd[5]={0}; |
365 | const double min_eps = 1e-8; |
366 | bool is_float = depth == CV_32F; |
367 | |
368 | AutoBuffer<double> _Ad(n*12+n); |
369 | double *Ad = _Ad.data(), *ud = Ad + n*5, *bd = ud + n*5; |
370 | Point2f* ptsf_copy = (Point2f*)(bd + n); |
371 | |
372 | // first fit for parameters A - E |
373 | Mat A( n, 5, CV_64F, Ad ); |
374 | Mat b( n, 1, CV_64F, bd ); |
375 | Mat x( 5, 1, CV_64F, gfp ); |
376 | Mat u( n, 1, CV_64F, ud ); |
377 | Mat vt( 5, 5, CV_64F, vd ); |
378 | Mat w( 5, 1, CV_64F, wd ); |
379 | |
380 | { |
381 | const Point* ptsi = points.ptr<Point>(); |
382 | const Point2f* ptsf = points.ptr<Point2f>(); |
383 | for( i = 0; i < n; i++ ) |
384 | { |
385 | Point2f p = is_float ? ptsf[i] : Point2f((float)ptsi[i].x, (float)ptsi[i].y); |
386 | ptsf_copy[i] = p; |
387 | c += p; |
388 | } |
389 | } |
390 | c.x /= n; |
391 | c.y /= n; |
392 | |
393 | double s = 0; |
394 | for( i = 0; i < n; i++ ) |
395 | { |
396 | Point2f p = ptsf_copy[i]; |
397 | p -= c; |
398 | s += fabs(x: p.x) + fabs(x: p.y); |
399 | } |
400 | double scale = 100./(s > FLT_EPSILON ? s : FLT_EPSILON); |
401 | |
402 | for( i = 0; i < n; i++ ) |
403 | { |
404 | Point2f p = ptsf_copy[i]; |
405 | p -= c; |
406 | double px = p.x*scale; |
407 | double py = p.y*scale; |
408 | |
409 | bd[i] = 10000.0; // 1.0? |
410 | Ad[i*5] = -px * px; // A - C signs inverted as proposed by APP |
411 | Ad[i*5 + 1] = -py * py; |
412 | Ad[i*5 + 2] = -px * py; |
413 | Ad[i*5 + 3] = px; |
414 | Ad[i*5 + 4] = py; |
415 | } |
416 | |
417 | SVDecomp(src: A, w, u, vt); |
418 | if(wd[0]*FLT_EPSILON > wd[4]) { |
419 | float eps = (float)(s/(n*2)*1e-3); |
420 | for( i = 0; i < n; i++ ) |
421 | { |
422 | Point2f p = ptsf_copy[i] + getOfs(i, eps); |
423 | ptsf_copy[i] = p; |
424 | } |
425 | |
426 | for( i = 0; i < n; i++ ) |
427 | { |
428 | Point2f p = ptsf_copy[i]; |
429 | p -= c; |
430 | double px = p.x*scale; |
431 | double py = p.y*scale; |
432 | bd[i] = 10000.0; // 1.0? |
433 | Ad[i*5] = -px * px; // A - C signs inverted as proposed by APP |
434 | Ad[i*5 + 1] = -py * py; |
435 | Ad[i*5 + 2] = -px * py; |
436 | Ad[i*5 + 3] = px; |
437 | Ad[i*5 + 4] = py; |
438 | } |
439 | SVDecomp(src: A, w, u, vt); |
440 | } |
441 | SVBackSubst(w, u, vt, rhs: b, dst: x); |
442 | |
443 | // now use general-form parameters A - E to find the ellipse center: |
444 | // differentiate general form wrt x/y to get two equations for cx and cy |
445 | A = Mat( 2, 2, CV_64F, Ad ); |
446 | b = Mat( 2, 1, CV_64F, bd ); |
447 | x = Mat( 2, 1, CV_64F, rp ); |
448 | Ad[0] = 2 * gfp[0]; |
449 | Ad[1] = Ad[2] = gfp[2]; |
450 | Ad[3] = 2 * gfp[1]; |
451 | bd[0] = gfp[3]; |
452 | bd[1] = gfp[4]; |
453 | solve( src1: A, src2: b, dst: x, flags: DECOMP_SVD ); |
454 | |
455 | // re-fit for parameters A - C with those center coordinates |
456 | A = Mat( n, 3, CV_64F, Ad ); |
457 | b = Mat( n, 1, CV_64F, bd ); |
458 | x = Mat( 3, 1, CV_64F, gfp ); |
459 | for( i = 0; i < n; i++ ) |
460 | { |
461 | Point2f p = ptsf_copy[i]; |
462 | p -= c; |
463 | double px = p.x*scale; |
464 | double py = p.y*scale; |
465 | bd[i] = 1.0; |
466 | Ad[i * 3] = (px - rp[0]) * (px - rp[0]); |
467 | Ad[i * 3 + 1] = (py - rp[1]) * (py - rp[1]); |
468 | Ad[i * 3 + 2] = (px - rp[0]) * (py - rp[1]); |
469 | } |
470 | solve(src1: A, src2: b, dst: x, flags: DECOMP_SVD); |
471 | |
472 | // store angle and radii |
473 | rp[4] = -0.5 * atan2(y: gfp[2], x: gfp[1] - gfp[0]); // convert from APP angle usage |
474 | if( fabs(x: gfp[2]) > min_eps ) |
475 | t = gfp[2]/sin(x: -2.0 * rp[4]); |
476 | else // ellipse is rotated by an integer multiple of pi/2 |
477 | t = gfp[1] - gfp[0]; |
478 | rp[2] = fabs(x: gfp[0] + gfp[1] - t); |
479 | if( rp[2] > min_eps ) |
480 | rp[2] = std::sqrt(x: 2.0 / rp[2]); |
481 | rp[3] = fabs(x: gfp[0] + gfp[1] + t); |
482 | if( rp[3] > min_eps ) |
483 | rp[3] = std::sqrt(x: 2.0 / rp[3]); |
484 | |
485 | box.center.x = (float)(rp[0]/scale) + c.x; |
486 | box.center.y = (float)(rp[1]/scale) + c.y; |
487 | box.size.width = (float)(rp[2]*2/scale); |
488 | box.size.height = (float)(rp[3]*2/scale); |
489 | if( box.size.width > box.size.height ) |
490 | { |
491 | float tmp; |
492 | CV_SWAP( box.size.width, box.size.height, tmp ); |
493 | box.angle = (float)(90 + rp[4]*180/CV_PI); |
494 | } |
495 | if( box.angle < -180 ) |
496 | box.angle += 360; |
497 | if( box.angle > 360 ) |
498 | box.angle -= 360; |
499 | |
500 | return box; |
501 | } |
502 | } |
503 | |
504 | cv::RotatedRect cv::fitEllipse( InputArray _points ) |
505 | { |
506 | CV_INSTRUMENT_REGION(); |
507 | |
508 | Mat points = _points.getMat(); |
509 | int n = points.checkVector(elemChannels: 2); |
510 | return n == 5 ? fitEllipseDirect(points) : fitEllipseNoDirect(points: points); |
511 | } |
512 | |
513 | cv::RotatedRect cv::fitEllipseAMS( InputArray _points ) |
514 | { |
515 | Mat points = _points.getMat(); |
516 | int i, n = points.checkVector(elemChannels: 2); |
517 | int depth = points.depth(); |
518 | CV_Assert( n >= 0 && (depth == CV_32F || depth == CV_32S)); |
519 | |
520 | RotatedRect box; |
521 | |
522 | if( n < 5 ) |
523 | CV_Error( cv::Error::StsBadSize, "There should be at least 5 points to fit the ellipse" ); |
524 | |
525 | Point2f c(0,0); |
526 | |
527 | bool is_float = depth == CV_32F; |
528 | const Point* ptsi = points.ptr<Point>(); |
529 | const Point2f* ptsf = points.ptr<Point2f>(); |
530 | |
531 | Mat A( n, 6, CV_64F); |
532 | Matx<double, 6, 6> DM; |
533 | Matx<double, 5, 5> M; |
534 | Matx<double, 5, 1> pVec; |
535 | Matx<double, 6, 1> coeffs; |
536 | |
537 | double x0, y0, a, b, theta; |
538 | |
539 | for( i = 0; i < n; i++ ) |
540 | { |
541 | Point2f p = is_float ? ptsf[i] : Point2f((float)ptsi[i].x, (float)ptsi[i].y); |
542 | c += p; |
543 | } |
544 | c.x /= (float)n; |
545 | c.y /= (float)n; |
546 | |
547 | double s = 0; |
548 | for( i = 0; i < n; i++ ) |
549 | { |
550 | Point2f p = is_float ? ptsf[i] : Point2f((float)ptsi[i].x, (float)ptsi[i].y); |
551 | s += fabs(x: p.x - c.x) + fabs(x: p.y - c.y); |
552 | } |
553 | double scale = 100./(s > FLT_EPSILON ? s : (double)FLT_EPSILON); |
554 | |
555 | for( i = 0; i < n; i++ ) |
556 | { |
557 | Point2f p = is_float ? ptsf[i] : Point2f((float)ptsi[i].x, (float)ptsi[i].y); |
558 | double px = (p.x - c.x)*scale, py = (p.y - c.y)*scale; |
559 | |
560 | A.at<double>(i0: i,i1: 0) = px*px; |
561 | A.at<double>(i0: i,i1: 1) = px*py; |
562 | A.at<double>(i0: i,i1: 2) = py*py; |
563 | A.at<double>(i0: i,i1: 3) = px; |
564 | A.at<double>(i0: i,i1: 4) = py; |
565 | A.at<double>(i0: i,i1: 5) = 1.0; |
566 | } |
567 | cv::mulTransposed( src: A, dst: DM, aTa: true, delta: noArray(), scale: 1.0, dtype: -1 ); |
568 | DM *= (1.0/n); |
569 | double dnm = ( DM(2,5)*(DM(0,5) + DM(2,5)) - (DM(1,5)*DM(1,5)) ); |
570 | double ddm = (4.*(DM(0,5) + DM(2,5))*( (DM(0,5)*DM(2,5)) - (DM(1,5)*DM(1,5)))); |
571 | double ddmm = (2.*(DM(0,5) + DM(2,5))*( (DM(0,5)*DM(2,5)) - (DM(1,5)*DM(1,5)))); |
572 | |
573 | M(0,0)=((-DM(0,0) + DM(0,2) + DM(0,5)*DM(0,5))*(DM(1,5)*DM(1,5)) + (-2*DM(0,1)*DM(1,5) + DM(0,5)*(DM(0,0) \ |
574 | - (DM(0,5)*DM(0,5)) + (DM(1,5)*DM(1,5))))*DM(2,5) + (DM(0,0) - (DM(0,5)*DM(0,5)))*(DM(2,5)*DM(2,5))) / ddm; |
575 | M(0,1)=((DM(1,5)*DM(1,5))*(-DM(0,1) + DM(1,2) + DM(0,5)*DM(1,5)) + (DM(0,1)*DM(0,5) - ((DM(0,5)*DM(0,5)) + 2*DM(1,1))*DM(1,5) + \ |
576 | (DM(1,5)*DM(1,5)*DM(1,5)))*DM(2,5) + (DM(0,1) - DM(0,5)*DM(1,5))*(DM(2,5)*DM(2,5))) / ddm; |
577 | M(0,2)=(-2*DM(1,2)*DM(1,5)*DM(2,5) - DM(0,5)*(DM(2,5)*DM(2,5))*(DM(0,5) + DM(2,5)) + DM(0,2)*dnm + \ |
578 | (DM(1,5)*DM(1,5))*(DM(2,2) + DM(2,5)*(DM(0,5) + DM(2,5))))/ddm; |
579 | M(0,3)=(DM(1,5)*(DM(1,5)*DM(2,3) - 2*DM(1,3)*DM(2,5)) + DM(0,3)*dnm) / ddm; |
580 | M(0,4)=(DM(1,5)*(DM(1,5)*DM(2,4) - 2*DM(1,4)*DM(2,5)) + DM(0,4)*dnm) / ddm; |
581 | M(1,0)=(-(DM(0,2)*DM(0,5)*DM(1,5)) + (2*DM(0,1)*DM(0,5) - DM(0,0)*DM(1,5))*DM(2,5))/ddmm; |
582 | M(1,1)=(-(DM(0,1)*DM(1,5)*DM(2,5)) + DM(0,5)*(-(DM(1,2)*DM(1,5)) + 2*DM(1,1)*DM(2,5)))/ddmm; |
583 | M(1,2)=(-(DM(0,2)*DM(1,5)*DM(2,5)) + DM(0,5)*(-(DM(1,5)*DM(2,2)) + 2*DM(1,2)*DM(2,5)))/ddmm; |
584 | M(1,3)=(-(DM(0,3)*DM(1,5)*DM(2,5)) + DM(0,5)*(-(DM(1,5)*DM(2,3)) + 2*DM(1,3)*DM(2,5)))/ddmm; |
585 | M(1,4)=(-(DM(0,4)*DM(1,5)*DM(2,5)) + DM(0,5)*(-(DM(1,5)*DM(2,4)) + 2*DM(1,4)*DM(2,5)))/ddmm; |
586 | M(2,0)=(-2*DM(0,1)*DM(0,5)*DM(1,5) + (DM(0,0) + (DM(0,5)*DM(0,5)))*(DM(1,5)*DM(1,5)) + DM(0,5)*(-(DM(0,5)*DM(0,5)) \ |
587 | + (DM(1,5)*DM(1,5)))*DM(2,5) - (DM(0,5)*DM(0,5))*(DM(2,5)*DM(2,5)) + DM(0,2)*(-(DM(1,5)*DM(1,5)) + DM(0,5)*(DM(0,5) + DM(2,5)))) / ddm; |
588 | M(2,1)=((DM(0,5)*DM(0,5))*(DM(1,2) - DM(1,5)*DM(2,5)) + (DM(1,5)*DM(1,5))*(DM(0,1) - DM(1,2) + DM(1,5)*DM(2,5)) \ |
589 | + DM(0,5)*(DM(1,2)*DM(2,5) + DM(1,5)*(-2*DM(1,1) + (DM(1,5)*DM(1,5)) - (DM(2,5)*DM(2,5))))) / ddm; |
590 | M(2,2)=((DM(0,5)*DM(0,5))*(DM(2,2) - (DM(2,5)*DM(2,5))) + (DM(1,5)*DM(1,5))*(DM(0,2) - DM(2,2) + (DM(2,5)*DM(2,5))) + \ |
591 | DM(0,5)*(-2*DM(1,2)*DM(1,5) + DM(2,5)*((DM(1,5)*DM(1,5)) + DM(2,2) - (DM(2,5)*DM(2,5))))) / ddm; |
592 | M(2,3)=((DM(1,5)*DM(1,5))*(DM(0,3) - DM(2,3)) + (DM(0,5)*DM(0,5))*DM(2,3) + DM(0,5)*(-2*DM(1,3)*DM(1,5) + DM(2,3)*DM(2,5))) / ddm; |
593 | M(2,4)=((DM(1,5)*DM(1,5))*(DM(0,4) - DM(2,4)) + (DM(0,5)*DM(0,5))*DM(2,4) + DM(0,5)*(-2*DM(1,4)*DM(1,5) + DM(2,4)*DM(2,5))) / ddm; |
594 | M(3,0)=DM(0,3); |
595 | M(3,1)=DM(1,3); |
596 | M(3,2)=DM(2,3); |
597 | M(3,3)=DM(3,3); |
598 | M(3,4)=DM(3,4); |
599 | M(4,0)=DM(0,4); |
600 | M(4,1)=DM(1,4); |
601 | M(4,2)=DM(2,4); |
602 | M(4,3)=DM(3,4); |
603 | M(4,4)=DM(4,4); |
604 | |
605 | if (fabs(x: cv::determinant(a: M)) > 1.0e-10) { |
606 | Mat eVal, eVec; |
607 | eigenNonSymmetric(src: M, eigenvalues: eVal, eigenvectors: eVec); |
608 | |
609 | // Select the eigen vector {a,b,c,d,e} which has the lowest eigenvalue |
610 | int minpos = 0; |
611 | double normi, normEVali, normMinpos, normEValMinpos; |
612 | normMinpos = sqrt(x: eVec.at<double>(i0: minpos,i1: 0)*eVec.at<double>(i0: minpos,i1: 0) + eVec.at<double>(i0: minpos,i1: 1)*eVec.at<double>(i0: minpos,i1: 1) + \ |
613 | eVec.at<double>(i0: minpos,i1: 2)*eVec.at<double>(i0: minpos,i1: 2) + eVec.at<double>(i0: minpos,i1: 3)*eVec.at<double>(i0: minpos,i1: 3) + \ |
614 | eVec.at<double>(i0: minpos,i1: 4)*eVec.at<double>(i0: minpos,i1: 4) ); |
615 | normEValMinpos = eVal.at<double>(i0: minpos,i1: 0) * normMinpos; |
616 | for (i=1; i<5; i++) { |
617 | normi = sqrt(x: eVec.at<double>(i0: i,i1: 0)*eVec.at<double>(i0: i,i1: 0) + eVec.at<double>(i0: i,i1: 1)*eVec.at<double>(i0: i,i1: 1) + \ |
618 | eVec.at<double>(i0: i,i1: 2)*eVec.at<double>(i0: i,i1: 2) + eVec.at<double>(i0: i,i1: 3)*eVec.at<double>(i0: i,i1: 3) + \ |
619 | eVec.at<double>(i0: i,i1: 4)*eVec.at<double>(i0: i,i1: 4) ); |
620 | normEVali = eVal.at<double>(i0: i,i1: 0) * normi; |
621 | if (normEVali < normEValMinpos) { |
622 | minpos = i; |
623 | normMinpos=normi; |
624 | normEValMinpos=normEVali; |
625 | } |
626 | }; |
627 | |
628 | pVec(0) =eVec.at<double>(i0: minpos,i1: 0) / normMinpos; |
629 | pVec(1) =eVec.at<double>(i0: minpos,i1: 1) / normMinpos; |
630 | pVec(2) =eVec.at<double>(i0: minpos,i1: 2) / normMinpos; |
631 | pVec(3) =eVec.at<double>(i0: minpos,i1: 3) / normMinpos; |
632 | pVec(4) =eVec.at<double>(i0: minpos,i1: 4) / normMinpos; |
633 | |
634 | coeffs(0) =pVec(0) ; |
635 | coeffs(1) =pVec(1) ; |
636 | coeffs(2) =pVec(2) ; |
637 | coeffs(3) =pVec(3) ; |
638 | coeffs(4) =pVec(4) ; |
639 | coeffs(5) =-pVec(0) *DM(0,5)-pVec(1) *DM(1,5)-coeffs(2) *DM(2,5); |
640 | |
641 | // Check that an elliptical solution has been found. AMS sometimes produces Parabolic solutions. |
642 | bool is_ellipse = (coeffs(0) < 0 && \ |
643 | coeffs(2) < (coeffs(1) *coeffs(1) )/(4.*coeffs(0) ) && \ |
644 | coeffs(5) > (-(coeffs(2) *(coeffs(3) *coeffs(3) )) + coeffs(1) *coeffs(3) *coeffs(4) - coeffs(0) *(coeffs(4) *coeffs(4) )) / \ |
645 | ((coeffs(1) *coeffs(1) ) - 4*coeffs(0) *coeffs(2) )) || \ |
646 | (coeffs(0) > 0 && \ |
647 | coeffs(2) > (coeffs(1) *coeffs(1) )/(4.*coeffs(0) ) && \ |
648 | coeffs(5) < (-(coeffs(2) *(coeffs(3) *coeffs(3) )) + coeffs(1) *coeffs(3) *coeffs(4) - coeffs(0) *(coeffs(4) *coeffs(4) )) / \ |
649 | ( (coeffs(1) *coeffs(1) ) - 4*coeffs(0) *coeffs(2) )); |
650 | if (is_ellipse) { |
651 | double u1 = pVec(2) *pVec(3) *pVec(3) - pVec(1) *pVec(3) *pVec(4) + pVec(0) *pVec(4) *pVec(4) + pVec(1) *pVec(1) *coeffs(5) ; |
652 | double u2 = pVec(0) *pVec(2) *coeffs(5) ; |
653 | double l1 = sqrt(x: pVec(1) *pVec(1) + (pVec(0) - pVec(2) )*(pVec(0) - pVec(2) )); |
654 | double l2 = pVec(0) + pVec(2) ; |
655 | double l3 = pVec(1) *pVec(1) - 4.0*pVec(0) *pVec(2) ; |
656 | double p1 = 2.0*pVec(2) *pVec(3) - pVec(1) *pVec(4) ; |
657 | double p2 = 2.0*pVec(0) *pVec(4) -(pVec(1) *pVec(3) ); |
658 | |
659 | x0 = p1/l3/scale + c.x; |
660 | y0 = p2/l3/scale + c.y; |
661 | a = std::sqrt(x: 2.)*sqrt(x: (u1 - 4.0*u2)/((l1 - l2)*l3))/scale; |
662 | b = std::sqrt(x: 2.)*sqrt(x: -1.0*((u1 - 4.0*u2)/((l1 + l2)*l3)))/scale; |
663 | if (pVec(1) == 0) { |
664 | if (pVec(0) < pVec(2) ) { |
665 | theta = 0; |
666 | } else { |
667 | theta = CV_PI/2.; |
668 | } |
669 | } else { |
670 | theta = CV_PI/2. + 0.5*std::atan2(y: pVec(1) , x: (pVec(0) - pVec(2) )); |
671 | } |
672 | |
673 | box.center.x = (float)x0; |
674 | box.center.y = (float)y0; |
675 | box.size.width = (float)(2.0*a); |
676 | box.size.height = (float)(2.0*b); |
677 | if( box.size.width > box.size.height ) |
678 | { |
679 | float tmp; |
680 | CV_SWAP( box.size.width, box.size.height, tmp ); |
681 | box.angle = (float)(90 + theta*180/CV_PI); |
682 | } else { |
683 | box.angle = (float)(fmod(x: theta*180/CV_PI,y: 180.0)); |
684 | }; |
685 | |
686 | |
687 | } else { |
688 | box = cv::fitEllipseDirect( points ); |
689 | } |
690 | } else { |
691 | box = cv::fitEllipseNoDirect( points: points ); |
692 | } |
693 | |
694 | return box; |
695 | } |
696 | |
697 | cv::RotatedRect cv::fitEllipseDirect( InputArray _points ) |
698 | { |
699 | Mat points = _points.getMat(); |
700 | int i, n = points.checkVector(elemChannels: 2); |
701 | int depth = points.depth(); |
702 | float eps = 0; |
703 | CV_Assert( n >= 0 && (depth == CV_32F || depth == CV_32S)); |
704 | |
705 | RotatedRect box; |
706 | |
707 | if( n < 5 ) |
708 | CV_Error( cv::Error::StsBadSize, "There should be at least 5 points to fit the ellipse" ); |
709 | |
710 | Point2d c(0., 0.); |
711 | |
712 | bool is_float = (depth == CV_32F); |
713 | const Point* ptsi = points.ptr<Point>(); |
714 | const Point2f* ptsf = points.ptr<Point2f>(); |
715 | |
716 | Mat A( n, 6, CV_64F); |
717 | Matx<double, 6, 6> DM; |
718 | Matx33d M, TM, Q; |
719 | Matx<double, 3, 1> pVec; |
720 | |
721 | double x0, y0, a, b, theta, Ts; |
722 | double s = 0; |
723 | |
724 | for( i = 0; i < n; i++ ) |
725 | { |
726 | Point2f p = is_float ? ptsf[i] : Point2f((float)ptsi[i].x, (float)ptsi[i].y); |
727 | c.x += p.x; |
728 | c.y += p.y; |
729 | } |
730 | c.x /= n; |
731 | c.y /= n; |
732 | |
733 | for( i = 0; i < n; i++ ) |
734 | { |
735 | Point2f p = is_float ? ptsf[i] : Point2f((float)ptsi[i].x, (float)ptsi[i].y); |
736 | s += fabs(x: p.x - c.x) + fabs(x: p.y - c.y); |
737 | } |
738 | double scale = 100./(s > FLT_EPSILON ? s : (double)FLT_EPSILON); |
739 | |
740 | // first, try the original pointset. |
741 | // if it's singular, try to shift the points a bit |
742 | int iter = 0; |
743 | for( iter = 0; iter < 2; iter++ ) { |
744 | for( i = 0; i < n; i++ ) |
745 | { |
746 | Point2f p = is_float ? ptsf[i] : Point2f((float)ptsi[i].x, (float)ptsi[i].y); |
747 | Point2f delta = getOfs(i, eps); |
748 | double px = (p.x + delta.x - c.x)*scale, py = (p.y + delta.y - c.y)*scale; |
749 | |
750 | A.at<double>(i0: i,i1: 0) = px*px; |
751 | A.at<double>(i0: i,i1: 1) = px*py; |
752 | A.at<double>(i0: i,i1: 2) = py*py; |
753 | A.at<double>(i0: i,i1: 3) = px; |
754 | A.at<double>(i0: i,i1: 4) = py; |
755 | A.at<double>(i0: i,i1: 5) = 1.0; |
756 | } |
757 | cv::mulTransposed( src: A, dst: DM, aTa: true, delta: noArray(), scale: 1.0, dtype: -1 ); |
758 | DM *= (1.0/n); |
759 | |
760 | TM(0,0) = DM(0,5)*DM(3,5)*DM(4,4) - DM(0,5)*DM(3,4)*DM(4,5) - DM(0,4)*DM(3,5)*DM(5,4) + \ |
761 | DM(0,3)*DM(4,5)*DM(5,4) + DM(0,4)*DM(3,4)*DM(5,5) - DM(0,3)*DM(4,4)*DM(5,5); |
762 | TM(0,1) = DM(1,5)*DM(3,5)*DM(4,4) - DM(1,5)*DM(3,4)*DM(4,5) - DM(1,4)*DM(3,5)*DM(5,4) + \ |
763 | DM(1,3)*DM(4,5)*DM(5,4) + DM(1,4)*DM(3,4)*DM(5,5) - DM(1,3)*DM(4,4)*DM(5,5); |
764 | TM(0,2) = DM(2,5)*DM(3,5)*DM(4,4) - DM(2,5)*DM(3,4)*DM(4,5) - DM(2,4)*DM(3,5)*DM(5,4) + \ |
765 | DM(2,3)*DM(4,5)*DM(5,4) + DM(2,4)*DM(3,4)*DM(5,5) - DM(2,3)*DM(4,4)*DM(5,5); |
766 | TM(1,0) = DM(0,5)*DM(3,3)*DM(4,5) - DM(0,5)*DM(3,5)*DM(4,3) + DM(0,4)*DM(3,5)*DM(5,3) - \ |
767 | DM(0,3)*DM(4,5)*DM(5,3) - DM(0,4)*DM(3,3)*DM(5,5) + DM(0,3)*DM(4,3)*DM(5,5); |
768 | TM(1,1) = DM(1,5)*DM(3,3)*DM(4,5) - DM(1,5)*DM(3,5)*DM(4,3) + DM(1,4)*DM(3,5)*DM(5,3) - \ |
769 | DM(1,3)*DM(4,5)*DM(5,3) - DM(1,4)*DM(3,3)*DM(5,5) + DM(1,3)*DM(4,3)*DM(5,5); |
770 | TM(1,2) = DM(2,5)*DM(3,3)*DM(4,5) - DM(2,5)*DM(3,5)*DM(4,3) + DM(2,4)*DM(3,5)*DM(5,3) - \ |
771 | DM(2,3)*DM(4,5)*DM(5,3) - DM(2,4)*DM(3,3)*DM(5,5) + DM(2,3)*DM(4,3)*DM(5,5); |
772 | TM(2,0) = DM(0,5)*DM(3,4)*DM(4,3) - DM(0,5)*DM(3,3)*DM(4,4) - DM(0,4)*DM(3,4)*DM(5,3) + \ |
773 | DM(0,3)*DM(4,4)*DM(5,3) + DM(0,4)*DM(3,3)*DM(5,4) - DM(0,3)*DM(4,3)*DM(5,4); |
774 | TM(2,1) = DM(1,5)*DM(3,4)*DM(4,3) - DM(1,5)*DM(3,3)*DM(4,4) - DM(1,4)*DM(3,4)*DM(5,3) + \ |
775 | DM(1,3)*DM(4,4)*DM(5,3) + DM(1,4)*DM(3,3)*DM(5,4) - DM(1,3)*DM(4,3)*DM(5,4); |
776 | TM(2,2) = DM(2,5)*DM(3,4)*DM(4,3) - DM(2,5)*DM(3,3)*DM(4,4) - DM(2,4)*DM(3,4)*DM(5,3) + \ |
777 | DM(2,3)*DM(4,4)*DM(5,3) + DM(2,4)*DM(3,3)*DM(5,4) - DM(2,3)*DM(4,3)*DM(5,4); |
778 | |
779 | Ts=(-(DM(3,5)*DM(4,4)*DM(5,3)) + DM(3,4)*DM(4,5)*DM(5,3) + DM(3,5)*DM(4,3)*DM(5,4) - \ |
780 | DM(3,3)*DM(4,5)*DM(5,4) - DM(3,4)*DM(4,3)*DM(5,5) + DM(3,3)*DM(4,4)*DM(5,5)); |
781 | |
782 | M(0,0) = (DM(2,0) + (DM(2,3)*TM(0,0) + DM(2,4)*TM(1,0) + DM(2,5)*TM(2,0))/Ts)/2.; |
783 | M(0,1) = (DM(2,1) + (DM(2,3)*TM(0,1) + DM(2,4)*TM(1,1) + DM(2,5)*TM(2,1))/Ts)/2.; |
784 | M(0,2) = (DM(2,2) + (DM(2,3)*TM(0,2) + DM(2,4)*TM(1,2) + DM(2,5)*TM(2,2))/Ts)/2.; |
785 | M(1,0) = -DM(1,0) - (DM(1,3)*TM(0,0) + DM(1,4)*TM(1,0) + DM(1,5)*TM(2,0))/Ts; |
786 | M(1,1) = -DM(1,1) - (DM(1,3)*TM(0,1) + DM(1,4)*TM(1,1) + DM(1,5)*TM(2,1))/Ts; |
787 | M(1,2) = -DM(1,2) - (DM(1,3)*TM(0,2) + DM(1,4)*TM(1,2) + DM(1,5)*TM(2,2))/Ts; |
788 | M(2,0) = (DM(0,0) + (DM(0,3)*TM(0,0) + DM(0,4)*TM(1,0) + DM(0,5)*TM(2,0))/Ts)/2.; |
789 | M(2,1) = (DM(0,1) + (DM(0,3)*TM(0,1) + DM(0,4)*TM(1,1) + DM(0,5)*TM(2,1))/Ts)/2.; |
790 | M(2,2) = (DM(0,2) + (DM(0,3)*TM(0,2) + DM(0,4)*TM(1,2) + DM(0,5)*TM(2,2))/Ts)/2.; |
791 | |
792 | double det = fabs(x: cv::determinant(a: M)); |
793 | if (fabs(x: det) > 1.0e-10) |
794 | break; |
795 | eps = (float)(s/(n*2)*1e-2); |
796 | } |
797 | |
798 | if( iter < 2 ) { |
799 | Mat eVal, eVec; |
800 | eigenNonSymmetric(src: M, eigenvalues: eVal, eigenvectors: eVec); |
801 | |
802 | // Select the eigen vector {a,b,c} which satisfies 4ac-b^2 > 0 |
803 | double cond[3]; |
804 | cond[0]=(4.0 * eVec.at<double>(i0: 0,i1: 0) * eVec.at<double>(i0: 0,i1: 2) - eVec.at<double>(i0: 0,i1: 1) * eVec.at<double>(i0: 0,i1: 1)); |
805 | cond[1]=(4.0 * eVec.at<double>(i0: 1,i1: 0) * eVec.at<double>(i0: 1,i1: 2) - eVec.at<double>(i0: 1,i1: 1) * eVec.at<double>(i0: 1,i1: 1)); |
806 | cond[2]=(4.0 * eVec.at<double>(i0: 2,i1: 0) * eVec.at<double>(i0: 2,i1: 2) - eVec.at<double>(i0: 2,i1: 1) * eVec.at<double>(i0: 2,i1: 1)); |
807 | if (cond[0]<cond[1]) { |
808 | i = (cond[1]<cond[2]) ? 2 : 1; |
809 | } else { |
810 | i = (cond[0]<cond[2]) ? 2 : 0; |
811 | } |
812 | double norm = std::sqrt(x: eVec.at<double>(i0: i,i1: 0)*eVec.at<double>(i0: i,i1: 0) + eVec.at<double>(i0: i,i1: 1)*eVec.at<double>(i0: i,i1: 1) + eVec.at<double>(i0: i,i1: 2)*eVec.at<double>(i0: i,i1: 2)); |
813 | if (((eVec.at<double>(i0: i,i1: 0)<0.0 ? -1 : 1) * (eVec.at<double>(i0: i,i1: 1)<0.0 ? -1 : 1) * (eVec.at<double>(i0: i,i1: 2)<0.0 ? -1 : 1)) <= 0.0) { |
814 | norm=-1.0*norm; |
815 | } |
816 | pVec(0) =eVec.at<double>(i0: i,i1: 0)/norm; pVec(1) =eVec.at<double>(i0: i,i1: 1)/norm;pVec(2) =eVec.at<double>(i0: i,i1: 2)/norm; |
817 | |
818 | // Q = (TM . pVec)/Ts; |
819 | Q(0,0) = (TM(0,0)*pVec(0) +TM(0,1)*pVec(1) +TM(0,2)*pVec(2) )/Ts; |
820 | Q(0,1) = (TM(1,0)*pVec(0) +TM(1,1)*pVec(1) +TM(1,2)*pVec(2) )/Ts; |
821 | Q(0,2) = (TM(2,0)*pVec(0) +TM(2,1)*pVec(1) +TM(2,2)*pVec(2) )/Ts; |
822 | |
823 | // We compute the ellipse properties in the shifted coordinates as doing so improves the numerical accuracy. |
824 | |
825 | double u1 = pVec(2)*Q(0,0)*Q(0,0) - pVec(1)*Q(0,0)*Q(0,1) + pVec(0)*Q(0,1)*Q(0,1) + pVec(1)*pVec(1)*Q(0,2); |
826 | double u2 = pVec(0)*pVec(2)*Q(0,2); |
827 | double l1 = sqrt(x: pVec(1)*pVec(1) + (pVec(0) - pVec(2))*(pVec(0) - pVec(2))); |
828 | double l2 = pVec(0) + pVec(2) ; |
829 | double l3 = pVec(1)*pVec(1) - 4*pVec(0)*pVec(2) ; |
830 | double p1 = 2*pVec(2)*Q(0,0) - pVec(1)*Q(0,1); |
831 | double p2 = 2*pVec(0)*Q(0,1) - pVec(1)*Q(0,0); |
832 | |
833 | x0 = (p1/l3/scale) + c.x; |
834 | y0 = (p2/l3/scale) + c.y; |
835 | a = sqrt(x: 2.)*sqrt(x: (u1 - 4.0*u2)/((l1 - l2)*l3))/scale; |
836 | b = sqrt(x: 2.)*sqrt(x: -1.0*((u1 - 4.0*u2)/((l1 + l2)*l3)))/scale; |
837 | if (pVec(1) == 0) { |
838 | if (pVec(0) < pVec(2) ) { |
839 | theta = 0; |
840 | } else { |
841 | theta = CV_PI/2.; |
842 | } |
843 | } else { |
844 | theta = CV_PI/2. + 0.5*std::atan2(y: pVec(1) , x: (pVec(0) - pVec(2) )); |
845 | } |
846 | |
847 | box.center.x = (float)x0; |
848 | box.center.y = (float)y0; |
849 | box.size.width = (float)(2.0*a); |
850 | box.size.height = (float)(2.0*b); |
851 | if( box.size.width > box.size.height ) |
852 | { |
853 | float tmp; |
854 | CV_SWAP( box.size.width, box.size.height, tmp ); |
855 | box.angle = (float)(fmod(x: (90 + theta*180/CV_PI),y: 180.0)) ; |
856 | } else { |
857 | box.angle = (float)(fmod(x: theta*180/CV_PI,y: 180.0)); |
858 | }; |
859 | } else { |
860 | box = cv::fitEllipseNoDirect( points: points ); |
861 | } |
862 | return box; |
863 | } |
864 | |
865 | ////////////////////////////////////////////// C API /////////////////////////////////////////// |
866 | |
867 | CV_IMPL int |
868 | cvMinEnclosingCircle( const void* array, CvPoint2D32f * _center, float *_radius ) |
869 | { |
870 | cv::AutoBuffer<double> abuf; |
871 | cv::Mat points = cv::cvarrToMat(arr: array, copyData: false, allowND: false, coiMode: 0, buf: &abuf); |
872 | cv::Point2f center; |
873 | float radius; |
874 | |
875 | cv::minEnclosingCircle(points: points, center&: center, radius&: radius); |
876 | if(_center) |
877 | *_center = cvPoint2D32f(pt: center); |
878 | if(_radius) |
879 | *_radius = radius; |
880 | return 1; |
881 | } |
882 | |
883 | static void |
884 | icvMemCopy( double **buf1, double **buf2, double **buf3, int *b_max ) |
885 | { |
886 | CV_Assert( (*buf1 != NULL || *buf2 != NULL) && *buf3 != NULL ); |
887 | |
888 | int bb = *b_max; |
889 | if( *buf2 == NULL ) |
890 | { |
891 | *b_max = 2 * (*b_max); |
892 | *buf2 = (double *)cvAlloc( size: (*b_max) * sizeof( double )); |
893 | |
894 | memcpy( dest: *buf2, src: *buf3, n: bb * sizeof( double )); |
895 | |
896 | *buf3 = *buf2; |
897 | cvFree( buf1 ); |
898 | *buf1 = NULL; |
899 | } |
900 | else |
901 | { |
902 | *b_max = 2 * (*b_max); |
903 | *buf1 = (double *) cvAlloc( size: (*b_max) * sizeof( double )); |
904 | |
905 | memcpy( dest: *buf1, src: *buf3, n: bb * sizeof( double )); |
906 | |
907 | *buf3 = *buf1; |
908 | cvFree( buf2 ); |
909 | *buf2 = NULL; |
910 | } |
911 | } |
912 | |
913 | |
914 | /* area of a contour sector */ |
915 | static double icvContourSecArea( CvSeq * contour, CvSlice slice ) |
916 | { |
917 | cv::Point pt; /* pointer to points */ |
918 | cv::Point pt_s, pt_e; /* first and last points */ |
919 | CvSeqReader reader; /* points reader of contour */ |
920 | |
921 | int p_max = 2, p_ind; |
922 | int lpt, flag, i; |
923 | double a00; /* unnormalized moments m00 */ |
924 | double xi, yi, xi_1, yi_1, x0, y0, dxy, sk, sk1, t; |
925 | double x_s, y_s, nx, ny, dx, dy, du, dv; |
926 | double eps = 1.e-5; |
927 | double *p_are1, *p_are2, *p_are; |
928 | double area = 0; |
929 | |
930 | CV_Assert( contour != NULL && CV_IS_SEQ_POINT_SET( contour )); |
931 | |
932 | lpt = cvSliceLength( slice, seq: contour ); |
933 | /*if( n2 >= n1 ) |
934 | lpt = n2 - n1 + 1; |
935 | else |
936 | lpt = contour->total - n1 + n2 + 1;*/ |
937 | |
938 | if( contour->total <= 0 || lpt <= 2 ) |
939 | return 0.; |
940 | |
941 | a00 = x0 = y0 = xi_1 = yi_1 = 0; |
942 | sk1 = 0; |
943 | flag = 0; |
944 | dxy = 0; |
945 | p_are1 = (double *) cvAlloc( size: p_max * sizeof( double )); |
946 | |
947 | p_are = p_are1; |
948 | p_are2 = NULL; |
949 | |
950 | cvStartReadSeq( seq: contour, reader: &reader, reverse: 0 ); |
951 | cvSetSeqReaderPos( reader: &reader, index: slice.start_index ); |
952 | { CvPoint pt_s_ = CV_STRUCT_INITIALIZER; CV_READ_SEQ_ELEM(pt_s_, reader); pt_s = pt_s_; } |
953 | p_ind = 0; |
954 | cvSetSeqReaderPos( reader: &reader, index: slice.end_index ); |
955 | { CvPoint pt_e_ = CV_STRUCT_INITIALIZER; CV_READ_SEQ_ELEM(pt_e_, reader); pt_e = pt_e_; } |
956 | |
957 | /* normal coefficients */ |
958 | nx = pt_s.y - pt_e.y; |
959 | ny = pt_e.x - pt_s.x; |
960 | cvSetSeqReaderPos( reader: &reader, index: slice.start_index ); |
961 | |
962 | while( lpt-- > 0 ) |
963 | { |
964 | { CvPoint pt_ = CV_STRUCT_INITIALIZER; CV_READ_SEQ_ELEM(pt_, reader); pt = pt_; } |
965 | |
966 | if( flag == 0 ) |
967 | { |
968 | xi_1 = (double) pt.x; |
969 | yi_1 = (double) pt.y; |
970 | x0 = xi_1; |
971 | y0 = yi_1; |
972 | sk1 = 0; |
973 | flag = 1; |
974 | } |
975 | else |
976 | { |
977 | xi = (double) pt.x; |
978 | yi = (double) pt.y; |
979 | |
980 | /**************** edges intersection examination **************************/ |
981 | sk = nx * (xi - pt_s.x) + ny * (yi - pt_s.y); |
982 | if( (fabs( x: sk ) < eps && lpt > 0) || sk * sk1 < -eps ) |
983 | { |
984 | if( fabs( x: sk ) < eps ) |
985 | { |
986 | dxy = xi_1 * yi - xi * yi_1; |
987 | a00 = a00 + dxy; |
988 | dxy = xi * y0 - x0 * yi; |
989 | a00 = a00 + dxy; |
990 | |
991 | if( p_ind >= p_max ) |
992 | icvMemCopy( buf1: &p_are1, buf2: &p_are2, buf3: &p_are, b_max: &p_max ); |
993 | |
994 | p_are[p_ind] = a00 / 2.; |
995 | p_ind++; |
996 | a00 = 0; |
997 | sk1 = 0; |
998 | x0 = xi; |
999 | y0 = yi; |
1000 | dxy = 0; |
1001 | } |
1002 | else |
1003 | { |
1004 | /* define intersection point */ |
1005 | dv = yi - yi_1; |
1006 | du = xi - xi_1; |
1007 | dx = ny; |
1008 | dy = -nx; |
1009 | if( fabs( x: du ) > eps ) |
1010 | t = ((yi_1 - pt_s.y) * du + dv * (pt_s.x - xi_1)) / |
1011 | (du * dy - dx * dv); |
1012 | else |
1013 | t = (xi_1 - pt_s.x) / dx; |
1014 | if( t > eps && t < 1 - eps ) |
1015 | { |
1016 | x_s = pt_s.x + t * dx; |
1017 | y_s = pt_s.y + t * dy; |
1018 | dxy = xi_1 * y_s - x_s * yi_1; |
1019 | a00 += dxy; |
1020 | dxy = x_s * y0 - x0 * y_s; |
1021 | a00 += dxy; |
1022 | if( p_ind >= p_max ) |
1023 | icvMemCopy( buf1: &p_are1, buf2: &p_are2, buf3: &p_are, b_max: &p_max ); |
1024 | |
1025 | p_are[p_ind] = a00 / 2.; |
1026 | p_ind++; |
1027 | |
1028 | a00 = 0; |
1029 | sk1 = 0; |
1030 | x0 = x_s; |
1031 | y0 = y_s; |
1032 | dxy = x_s * yi - xi * y_s; |
1033 | } |
1034 | } |
1035 | } |
1036 | else |
1037 | dxy = xi_1 * yi - xi * yi_1; |
1038 | |
1039 | a00 += dxy; |
1040 | xi_1 = xi; |
1041 | yi_1 = yi; |
1042 | sk1 = sk; |
1043 | |
1044 | } |
1045 | } |
1046 | |
1047 | xi = x0; |
1048 | yi = y0; |
1049 | dxy = xi_1 * yi - xi * yi_1; |
1050 | |
1051 | a00 += dxy; |
1052 | |
1053 | if( p_ind >= p_max ) |
1054 | icvMemCopy( buf1: &p_are1, buf2: &p_are2, buf3: &p_are, b_max: &p_max ); |
1055 | |
1056 | p_are[p_ind] = a00 / 2.; |
1057 | p_ind++; |
1058 | |
1059 | // common area calculation |
1060 | area = 0; |
1061 | for( i = 0; i < p_ind; i++ ) |
1062 | area += fabs( x: p_are[i] ); |
1063 | |
1064 | if( p_are1 != NULL ) |
1065 | cvFree( &p_are1 ); |
1066 | else if( p_are2 != NULL ) |
1067 | cvFree( &p_are2 ); |
1068 | |
1069 | return area; |
1070 | } |
1071 | |
1072 | |
1073 | /* external contour area function */ |
1074 | CV_IMPL double |
1075 | cvContourArea( const void *array, CvSlice slice, int oriented ) |
1076 | { |
1077 | double area = 0; |
1078 | |
1079 | CvContour ; |
1080 | CvSeq* contour = 0; |
1081 | CvSeqBlock block; |
1082 | |
1083 | if( CV_IS_SEQ( array )) |
1084 | { |
1085 | contour = (CvSeq*)array; |
1086 | if( !CV_IS_SEQ_POLYLINE( contour )) |
1087 | CV_Error( cv::Error::StsBadArg, "Unsupported sequence type" ); |
1088 | } |
1089 | else |
1090 | { |
1091 | contour = cvPointSeqFromMat( CV_SEQ_KIND_CURVE, mat: array, contour_header: &contour_header, block: &block ); |
1092 | } |
1093 | |
1094 | if( cvSliceLength( slice, seq: contour ) == contour->total ) |
1095 | { |
1096 | cv::AutoBuffer<double> abuf; |
1097 | cv::Mat points = cv::cvarrToMat(arr: contour, copyData: false, allowND: false, coiMode: 0, buf: &abuf); |
1098 | return cv::contourArea( contour: points, oriented: oriented !=0 ); |
1099 | } |
1100 | |
1101 | if( CV_SEQ_ELTYPE( contour ) != CV_32SC2 ) |
1102 | CV_Error( cv::Error::StsUnsupportedFormat, |
1103 | "Only curves with integer coordinates are supported in case of contour slice" ); |
1104 | area = icvContourSecArea( contour, slice ); |
1105 | return oriented ? area : fabs(x: area); |
1106 | } |
1107 | |
1108 | |
1109 | /* calculates length of a curve (e.g. contour perimeter) */ |
1110 | CV_IMPL double |
1111 | cvArcLength( const void *array, CvSlice slice, int is_closed ) |
1112 | { |
1113 | double perimeter = 0; |
1114 | |
1115 | int i, j = 0, count; |
1116 | const int N = 16; |
1117 | float buf[N]; |
1118 | CvMat buffer = cvMat( rows: 1, cols: N, CV_32F, data: buf ); |
1119 | CvSeqReader reader; |
1120 | CvContour ; |
1121 | CvSeq* contour = 0; |
1122 | CvSeqBlock block; |
1123 | |
1124 | if( CV_IS_SEQ( array )) |
1125 | { |
1126 | contour = (CvSeq*)array; |
1127 | if( !CV_IS_SEQ_POLYLINE( contour )) |
1128 | CV_Error( cv::Error::StsBadArg, "Unsupported sequence type" ); |
1129 | if( is_closed < 0 ) |
1130 | is_closed = CV_IS_SEQ_CLOSED( contour ); |
1131 | } |
1132 | else |
1133 | { |
1134 | is_closed = is_closed > 0; |
1135 | contour = cvPointSeqFromMat( |
1136 | CV_SEQ_KIND_CURVE | (is_closed ? CV_SEQ_FLAG_CLOSED : 0), |
1137 | mat: array, contour_header: &contour_header, block: &block ); |
1138 | } |
1139 | |
1140 | if( contour->total > 1 ) |
1141 | { |
1142 | int is_float = CV_SEQ_ELTYPE( contour ) == CV_32FC2; |
1143 | |
1144 | cvStartReadSeq( seq: contour, reader: &reader, reverse: 0 ); |
1145 | cvSetSeqReaderPos( reader: &reader, index: slice.start_index ); |
1146 | count = cvSliceLength( slice, seq: contour ); |
1147 | |
1148 | count -= !is_closed && count == contour->total; |
1149 | |
1150 | // scroll the reader by 1 point |
1151 | reader.prev_elem = reader.ptr; |
1152 | CV_NEXT_SEQ_ELEM( sizeof(CvPoint), reader ); |
1153 | |
1154 | for( i = 0; i < count; i++ ) |
1155 | { |
1156 | float dx, dy; |
1157 | |
1158 | if( !is_float ) |
1159 | { |
1160 | CvPoint* pt = (CvPoint*)reader.ptr; |
1161 | CvPoint* prev_pt = (CvPoint*)reader.prev_elem; |
1162 | |
1163 | dx = (float)pt->x - (float)prev_pt->x; |
1164 | dy = (float)pt->y - (float)prev_pt->y; |
1165 | } |
1166 | else |
1167 | { |
1168 | CvPoint2D32f* pt = (CvPoint2D32f*)reader.ptr; |
1169 | CvPoint2D32f* prev_pt = (CvPoint2D32f*)reader.prev_elem; |
1170 | |
1171 | dx = pt->x - prev_pt->x; |
1172 | dy = pt->y - prev_pt->y; |
1173 | } |
1174 | |
1175 | reader.prev_elem = reader.ptr; |
1176 | CV_NEXT_SEQ_ELEM( contour->elem_size, reader ); |
1177 | // Bugfix by Axel at rubico.com 2010-03-22, affects closed slices only |
1178 | // wraparound not handled by CV_NEXT_SEQ_ELEM |
1179 | if( is_closed && i == count - 2 ) |
1180 | cvSetSeqReaderPos( reader: &reader, index: slice.start_index ); |
1181 | |
1182 | buffer.data.fl[j] = dx * dx + dy * dy; |
1183 | if( ++j == N || i == count - 1 ) |
1184 | { |
1185 | buffer.cols = j; |
1186 | cvPow( src: &buffer, dst: &buffer, power: 0.5 ); |
1187 | for( ; j > 0; j-- ) |
1188 | perimeter += buffer.data.fl[j-1]; |
1189 | } |
1190 | } |
1191 | } |
1192 | |
1193 | return perimeter; |
1194 | } |
1195 | |
1196 | |
1197 | CV_IMPL CvBox2D |
1198 | cvFitEllipse2( const CvArr* array ) |
1199 | { |
1200 | cv::AutoBuffer<double> abuf; |
1201 | cv::Mat points = cv::cvarrToMat(arr: array, copyData: false, allowND: false, coiMode: 0, buf: &abuf); |
1202 | return cvBox2D(rr: cv::fitEllipse(points: points)); |
1203 | } |
1204 | |
1205 | /* End of file. */ |
1206 | |