1 // This file is part of OpenCV project.
2 // It is subject to the license terms in the LICENSE file found in the top-level directory
3 // of this distribution and at http://opencv.org/license.html.
4 
5 #ifndef __OPENCV_EDGE_DRAWING_COMMON_HPP__
6 #define __OPENCV_EDGE_DRAWING_COMMON_HPP__
7 
8 #include <opencv2/core.hpp>
9 
10 #define EDGE_VERTICAL   1
11 #define EDGE_HORIZONTAL 2
12 
13 #define ANCHOR_PIXEL  254
14 #define EDGE_PIXEL    255
15 
16 #define LEFT  1
17 #define RIGHT 2
18 #define UP    3
19 #define DOWN  4
20 
21 #define SOUTH_SOUTH 0
22 #define SOUTH_EAST 1
23 #define EAST_SOUTH 2
24 #define EAST_EAST 3
25 
26 // Circular arc, circle thresholds
27 #define VERY_SHORT_ARC_ERROR     0.40  // Used for very short arcs (>= CANDIDATE_CIRCLE_RATIO1 && < CANDIDATE_CIRCLE_RATIO2)
28 #define SHORT_ARC_ERROR          1.00  // Used for short arcs (>= CANDIDATE_CIRCLE_RATIO2 && < HALF_CIRCLE_RATIO)
29 #define HALF_ARC_ERROR           1.25  // Used for arcs with length (>=HALF_CIRCLE_RATIO && < FULL_CIRCLE_RATIO)
30 #define LONG_ARC_ERROR           1.50  // Used for long arcs (>= FULL_CIRCLE_RATIO)
31 
32 #define CANDIDATE_CIRCLE_RATIO1  0.25  // 25% -- If only 25% of the circle is detected, it may be a candidate for validation
33 #define CANDIDATE_CIRCLE_RATIO2  0.33  // 33% -- If only 33% of the circle is detected, it may be a candidate for validation
34 #define HALF_CIRCLE_RATIO        0.50  // 50% -- If 50% of a circle is detected at any point during joins, we immediately make it a candidate
35 #define FULL_CIRCLE_RATIO        0.67  // 67% -- If 67% of the circle is detected, we assume that it is fully covered
36 
37 // Ellipse thresholds
38 #define CANDIDATE_ELLIPSE_RATIO  0.50  // 50% -- If 50% of the ellipse is detected, it may be candidate for validation
39 #define ELLIPSE_ERROR            1.50  // Used for ellipses. (used to be 1.65 for what reason?)
40 #define MAX_GRAD_VALUE 128*256
41 
42 using namespace std;
43 using namespace cv;
44 
45 class NFALUT
46 {
47 public:
48 
49     NFALUT(int size, double _prob, int _w, int _h);
50     ~NFALUT();
51 
52     int* LUT; // look up table
53     int LUTSize;
54 
55     double prob;
56     int w, h;
57 
58     bool checkValidationByNFA(int n, int k);
59     static double myAtan2(double yy, double xx);
60 
61 private:
62     double nfa(int n, int k);
63     static double Comb(double n, double k);
64 };
65 
NFALUT(int size,double _prob,int _w,int _h)66 NFALUT::NFALUT(int size, double _prob, int _w, int _h)
67 {
68     LUTSize = size > 60 ? 60 : size;
69     LUT = new int[LUTSize];
70     w = _w;
71     h = _h;
72     prob = _prob;
73 
74     LUT[0] = 1;
75     int j = 1;
76     for (int i = 1; i < LUTSize; i++)
77     {
78         LUT[i] = LUTSize + 1;
79         double ret = nfa(i, j);
80         if (ret >= 1.0)
81         {
82             while (j < i)
83             {
84                 j++;
85                 ret = nfa(i, j);
86                 if (ret <= 1.0)
87                     break;
88             }
89 
90             if (ret >= 1.0)
91                 continue;
92         }
93         LUT[i] = j;
94     }
95 }
96 
97 
~NFALUT()98 NFALUT::~NFALUT()
99 {
100     delete[] LUT;
101 }
102 
checkValidationByNFA(int n,int k)103 bool NFALUT::checkValidationByNFA(int n, int k)
104 {
105     if (n >= LUTSize)
106         return nfa(n, k) <= 1.0;
107     else
108         return k >= LUT[n];
109 }
110 
myAtan2(double yy,double xx)111 double NFALUT::myAtan2(double yy, double xx)
112 {
113     double angle = fastAtan2((float)yy, (float)xx);
114 
115     if (angle > 180)
116         angle = angle - 180;
117 
118     return angle / 180 * CV_PI;
119 }
120 
Comb(double n,double k)121 double NFALUT::Comb(double n, double k)   //fast combination computation
122 {
123     if (k > n)
124         return 0;
125 
126     double r = 1;
127     for (double d = 1; d <= k; ++d)
128     {
129         r *= n--;
130         r /= d;
131     }
132     return r;
133 }
134 
nfa(int n,int k)135 double NFALUT::nfa(int n, int k)
136 {
137     double sum = 0;
138     double p = 0.125;
139     for (int i = k; i <= n; i++)
140         sum += Comb(n, i) * pow(p, i) * pow(1 - p, n - i);
141 
142     return sum * w * w * h * h;
143 }
144 
145 struct StackNode
146 {
147     int r, c;         // starting pixel
148     int parent;       // parent chain (-1 if no parent)
149     int dir;          // direction where you are supposed to go
150 };
151 
152 // Used during Edge Linking
153 struct Chain
154 {
155     int dir;          // Direction of the chain
156     int len;          // # of pixels in the chain
157     int parent;       // Parent of this node (-1 if no parent)
158     int children[2];  // Children of this node (-1 if no children)
159     Point *pixels;    // Pointer to the beginning of the pixels array
160 };
161 
162 // light weight struct for Start & End coordinates of the line segment
163 struct LS
164 {
165     Point2d start;
166     Point2d end;
167 
LSLS168     LS(Point2d _start, Point2d _end)
169     {
170         start = _start;
171         end = _end;
172     }
173 };
174 
175 
176 struct EDLineSegment
177 {
178     double a, b;          // y = a + bx (if invert = 0) || x = a + by (if invert = 1)
179     int invert;
180 
181     double sx, sy;        // starting x & y coordinates
182     double ex, ey;        // ending x & y coordinates
183 
184     int segmentNo;        // Edge segment that this line belongs to
185     int firstPixelIndex;  // Index of the first pixel within the segment of pixels
186     int len;              // No of pixels making up the line segment
187 
EDLineSegmentEDLineSegment188     EDLineSegment(double _a, double _b, int _invert, double _sx, double _sy, double _ex, double _ey, int _segmentNo, int _firstPixelIndex, int _len)
189     {
190         a = _a;
191         b = _b;
192         invert = _invert;
193         sx = _sx;
194         sy = _sy;
195         ex = _ex;
196         ey = _ey;
197         segmentNo = _segmentNo;
198         firstPixelIndex = _firstPixelIndex;
199         len = _len;
200     }
201 };
202 
203 // Circle equation: (x-xc)^2 + (y-yc)^2 = r^2
204 struct mCircle {
205 	Point2d center;
206 	double r;
mCirclemCircle207 	mCircle(Point2d _center, double _r) { center = _center; r = _r; }
208 };
209 
210 // Ellipse equation: Ax^2 + Bxy + Cy^2 + Dx + Ey + F = 0
211 struct mEllipse {
212 	Point2d center;
213 	Size axes;
214 	double theta;
mEllipsemEllipse215 	mEllipse(Point2d _center, Size _axes, double _theta) { center = _center; axes = _axes; theta = _theta; }
216 };
217 
218 //----------------------------------------------------------
219 // Ellipse Equation is of the form:
220 // Ax^2 + Bxy + Cy^2 + Dx + Ey + F = 0
221 //
222 struct EllipseEquation {
223 	double coeff[7];  // coeff[1] = A
224 
EllipseEquationEllipseEquation225 	EllipseEquation() {
226 		for (int i = 0; i<7; i++) coeff[i] = 0;
227 	}
228 
AEllipseEquation229 	double A() { return coeff[1]; }
BEllipseEquation230 	double B() { return coeff[2]; }
CEllipseEquation231 	double C() { return coeff[3]; }
DEllipseEquation232 	double D() { return coeff[4]; }
EEllipseEquation233 	double E() { return coeff[5]; }
FEllipseEquation234 	double F() { return coeff[6]; }
235 };
236 
237 // ================================ CIRCLES ================================
238 struct Circle {
239 	double xc, yc, r;        // Center (xc, yc) & radius.
240 	double circleFitError;   // circle fit error
241 	double coverRatio;       // Percentage of the circle covered by the arcs making up this circle [0-1]
242 
243 	double *x, *y;           // Pointers to buffers containing the pixels making up this circle
244 	int noPixels;            // # of pixels making up this circle
245 
246 							 // If this circle is better approximated by an ellipse, we set isEllipse to true & eq contains the ellipse's equation
247 	EllipseEquation eq;
248 	double ellipseFitError;  // ellipse fit error
249 	bool isEllipse;
250 	double majorAxisLength;     // Length of the major axis
251 	double minorAxisLength;     // Length of the minor axis
252 };
253 
254 // ------------------------------------------- ARCS ----------------------------------------------------
255 struct MyArc {
256 	double xc, yc, r;           // center x, y and radius
257 	double circleFitError;      // Error during circle fit
258 
259 	double sTheta, eTheta;      // Start & end angle in radius
260 	double coverRatio;          // Ratio of the pixels covered on the covering circle [0-1] (noPixels/circumference)
261 
262 	int turn;                   // Turn direction: 1 or -1
263 
264 	int segmentNo;              // SegmentNo where this arc belongs
265 
266 	int sx, sy;                 // Start (x, y) coordinate
267 	int ex, ey;                 // End (x, y) coordinate of the arc
268 
269 	double *x, *y;              // Pointer to buffer containing the pixels making up this arc
270 	int noPixels;               // # of pixels making up the arc
271 
272 	bool isEllipse;             // Did we fit an ellipse to this arc?
273 	EllipseEquation eq;         // If an ellipse, then the ellipse's equation
274 	double ellipseFitError;     // Error during ellipse fit
275 };
276 
277 // =============================== AngleSet ==================================
278 
279 // add a circular arc to the list of arcs
ArcLength(double sTheta,double eTheta)280 inline double ArcLength(double sTheta, double eTheta)
281 {
282 	if (eTheta > sTheta)
283         return eTheta - sTheta;
284 	else
285         return CV_2PI - sTheta + eTheta;
286 }
287 
288 // A fast implementation of the AngleSet class. The slow implementation is really bad. About 10 times slower than this!
289 struct AngleSetArc {
290 	double sTheta;
291 	double eTheta;
292 	int next;         // Next AngleSetArc in the linked list
293 };
294 
295 struct AngleSet {
296 	AngleSetArc angles[360];
297 	int head;
298 	int next;   // Next AngleSetArc to be allocated
299 	double overlapAmount;   // Total overlap of the arcs in angleSet. Computed during set() function
300 
AngleSetAngleSet301 	AngleSet() { clear(); }
clearAngleSet302 	void clear() { head = -1; next = 0; overlapAmount = 0; }
overlapRatioAngleSet303 	double overlapRatio() { return overlapAmount / CV_2PI; }
304 
305 	void _set(double sTheta, double eTheta);
306 	void set(double sTheta, double eTheta);
307 
308 	double _overlap(double sTheta, double eTheta);
309 	double overlap(double sTheta, double eTheta);
310 
311 	void computeStartEndTheta(double& sTheta, double& eTheta);
312 	double coverRatio();
313 };
314 
_set(double sTheta,double eTheta)315 void AngleSet::_set(double sTheta, double eTheta)
316 {
317     int arc = next++;
318 
319     angles[arc].sTheta = sTheta;
320     angles[arc].eTheta = eTheta;
321     angles[arc].next = -1;
322 
323     // Add the current arc to the linked list
324     int prev = -1;
325     int current = head;
326     while (1)
327     {
328         // Empty list?
329         if (head < 0)
330         {
331             head = arc;
332             break;
333         }
334 
335         // End of the list. Add to the end
336         if (current < 0)
337         {
338             CV_Assert(prev >= 0);
339             angles[prev].next = arc;
340             break;
341         }
342 
343         if (angles[arc].eTheta <= angles[current].sTheta)
344         {
345             // Add before current
346             if (prev < 0)
347             {
348                 angles[arc].next = current;
349                 head = arc;
350             }
351             else
352             {
353                 angles[arc].next = current;
354                 angles[prev].next = arc;
355             }
356             break;
357         }
358         else if (angles[arc].sTheta >= angles[current].eTheta)
359         {
360             // continue
361             prev = current;
362             current = angles[current].next;
363 
364             // End of the list?
365             if (current < 0)
366             {
367                 angles[prev].next = arc;
368                 break;
369             }
370         }
371         else
372         {
373             // overlaps with current. Join
374             // First delete current from the list
375             if (prev < 0)
376                 head = angles[head].next;
377             else
378                 angles[prev].next = angles[current].next;
379 
380             // Update overlap amount.
381             if (angles[arc].eTheta < angles[current].eTheta)
382             {
383                 overlapAmount += angles[arc].eTheta - angles[current].sTheta;
384             }
385             else
386             {
387                 overlapAmount += angles[current].eTheta - angles[arc].sTheta;
388             }
389 
390             // Now join current with arc
391             if (angles[current].sTheta < angles[arc].sTheta)
392                 angles[arc].sTheta = angles[current].sTheta;
393             if (angles[current].eTheta > angles[arc].eTheta)
394                 angles[arc].eTheta = angles[current].eTheta;
395             current = angles[current].next;
396         }
397     }
398 }
399 
set(double sTheta,double eTheta)400 void AngleSet::set(double sTheta, double eTheta)
401 {
402     if (eTheta > sTheta)
403     {
404         _set(sTheta, eTheta);
405     }
406     else
407     {
408         _set(sTheta, CV_2PI);
409         _set(0, eTheta);
410     }
411 }
412 
_overlap(double sTheta,double eTheta)413 double AngleSet::_overlap(double sTheta, double eTheta)
414 {
415     double o = 0;
416 
417     int current = head;
418     while (current >= 0)
419     {
420         if (sTheta > angles[current].eTheta)
421         {
422             current = angles[current].next;
423             continue;
424         }
425         else if (eTheta < angles[current].sTheta)
426             break;
427 
428         // 3 cases.
429         if (sTheta < angles[current].sTheta && eTheta > angles[current].eTheta)
430         {
431             o += angles[current].eTheta - angles[current].sTheta;
432 
433         }
434         else if (sTheta < angles[current].sTheta)
435         {
436             o += eTheta - angles[current].sTheta;
437         }
438         else
439         {
440             o += angles[current].eTheta - sTheta;
441         }
442 
443         current = angles[current].next;
444     }
445     return o;
446 }
447 
overlap(double sTheta,double eTheta)448 double AngleSet::overlap(double sTheta, double eTheta)
449 {
450     double o;
451 
452     if (eTheta > sTheta)
453     {
454         o = _overlap(sTheta, eTheta);
455     }
456     else
457     {
458         o = _overlap(sTheta, CV_2PI);
459         o += _overlap(0, eTheta);
460     }
461     return o / ArcLength(sTheta, eTheta);
462 }
463 
computeStartEndTheta(double & sTheta,double & eTheta)464 void AngleSet::computeStartEndTheta(double& sTheta, double& eTheta)
465 {
466     // Special case: Just one arc
467     if (angles[head].next < 0)
468     {
469         sTheta = angles[head].sTheta;
470         eTheta = angles[head].eTheta;
471 
472         return;
473     }
474 
475     // OK. More than one arc. Find the biggest gap
476     int current = head;
477     int nextArc = angles[current].next;
478 
479     double biggestGapSTheta = angles[current].eTheta;
480     double biggestGapEtheta = angles[nextArc].sTheta;
481     double biggestGapLength = biggestGapEtheta - biggestGapSTheta;
482 
483     double start, end, len;
484     while (1)
485     {
486         current = nextArc;
487         nextArc = angles[nextArc].next;
488         if (nextArc < 0)
489             break;
490 
491         start = angles[current].eTheta;
492         end = angles[nextArc].sTheta;
493         len = end - start;
494 
495         if (len > biggestGapLength)
496         {
497             biggestGapSTheta = start;
498             biggestGapEtheta = end;
499             biggestGapLength = len;
500         }
501     }
502 
503     // Compute the gap between the last arc & the first arc
504     start = angles[current].eTheta;
505     end = angles[head].sTheta;
506     len = CV_2PI - start + end;
507     if (len > biggestGapLength)
508     {
509         biggestGapSTheta = start;
510         biggestGapEtheta = end;
511     }
512     sTheta = biggestGapEtheta;
513     eTheta = biggestGapSTheta;
514 }
515 
coverRatio()516 double AngleSet::coverRatio()
517 {
518     int current = head;
519 
520     double total = 0;
521     while (current >= 0)
522     {
523         total += angles[current].eTheta - angles[current].sTheta;
524         current = angles[current].next;
525     }
526     return total / CV_2PI;
527 }
528 
529 struct EDArcs {
530 	MyArc *arcs;
531 	int noArcs;
532 
533 public:
EDArcsEDArcs534 	EDArcs(int size = 10000) {
535 		arcs = new MyArc[size];
536 		noArcs = 0;
537 	}
538 
~EDArcsEDArcs539 	~EDArcs() {
540 		delete arcs;
541 	}
542 };
543 
544 struct BufferManager {
545 	double *x, *y;
546 	int index;
547 
BufferManagerBufferManager548 	BufferManager(int maxSize) {
549 		x = new double[maxSize];
550 		y = new double[maxSize];
551 		index = 0;
552 	}
553 
~BufferManagerBufferManager554 	~BufferManager() {
555 		delete x;
556 		delete y;
557 	}
558 
getXBufferManager559 	double *getX() { return &x[index]; }
getYBufferManager560 	double *getY() { return &y[index]; }
moveBufferManager561 	void move(int size) { index += size; }
562 };
563 
564 struct Info {
565 	int sign;     // -1 or 1: sign of the cross product
566 	double angle; // angle with the next line (in radians)
567 	bool taken;   // Is this line taken during arc detection
568 };
569 
570 #endif
571