You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
570 lines
14 KiB
570 lines
14 KiB
// This file is part of OpenCV project. |
|
// It is subject to the license terms in the LICENSE file found in the top-level directory |
|
// of this distribution and at http://opencv.org/license.html. |
|
|
|
#ifndef __OPENCV_EDGE_DRAWING_COMMON_HPP__ |
|
#define __OPENCV_EDGE_DRAWING_COMMON_HPP__ |
|
|
|
#include <opencv2/core.hpp> |
|
|
|
#define EDGE_VERTICAL 1 |
|
#define EDGE_HORIZONTAL 2 |
|
|
|
#define ANCHOR_PIXEL 254 |
|
#define EDGE_PIXEL 255 |
|
|
|
#define LEFT 1 |
|
#define RIGHT 2 |
|
#define UP 3 |
|
#define DOWN 4 |
|
|
|
#define SOUTH_SOUTH 0 |
|
#define SOUTH_EAST 1 |
|
#define EAST_SOUTH 2 |
|
#define EAST_EAST 3 |
|
|
|
// Circular arc, circle thresholds |
|
#define VERY_SHORT_ARC_ERROR 0.40 // Used for very short arcs (>= CANDIDATE_CIRCLE_RATIO1 && < CANDIDATE_CIRCLE_RATIO2) |
|
#define SHORT_ARC_ERROR 1.00 // Used for short arcs (>= CANDIDATE_CIRCLE_RATIO2 && < HALF_CIRCLE_RATIO) |
|
#define HALF_ARC_ERROR 1.25 // Used for arcs with length (>=HALF_CIRCLE_RATIO && < FULL_CIRCLE_RATIO) |
|
#define LONG_ARC_ERROR 1.50 // Used for long arcs (>= FULL_CIRCLE_RATIO) |
|
|
|
#define CANDIDATE_CIRCLE_RATIO1 0.25 // 25% -- If only 25% of the circle is detected, it may be a candidate for validation |
|
#define CANDIDATE_CIRCLE_RATIO2 0.33 // 33% -- If only 33% of the circle is detected, it may be a candidate for validation |
|
#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 |
|
#define FULL_CIRCLE_RATIO 0.67 // 67% -- If 67% of the circle is detected, we assume that it is fully covered |
|
|
|
// Ellipse thresholds |
|
#define CANDIDATE_ELLIPSE_RATIO 0.50 // 50% -- If 50% of the ellipse is detected, it may be candidate for validation |
|
#define ELLIPSE_ERROR 1.50 // Used for ellipses. (used to be 1.65 for what reason?) |
|
#define MAX_GRAD_VALUE 128*256 |
|
|
|
using namespace std; |
|
using namespace cv; |
|
|
|
class NFALUT |
|
{ |
|
public: |
|
|
|
NFALUT(int size, double _prob, int _w, int _h); |
|
~NFALUT(); |
|
|
|
int* LUT; // look up table |
|
int LUTSize; |
|
|
|
double prob; |
|
int w, h; |
|
|
|
bool checkValidationByNFA(int n, int k); |
|
static double myAtan2(double yy, double xx); |
|
|
|
private: |
|
double nfa(int n, int k); |
|
static double Comb(double n, double k); |
|
}; |
|
|
|
NFALUT::NFALUT(int size, double _prob, int _w, int _h) |
|
{ |
|
LUTSize = size > 60 ? 60 : size; |
|
LUT = new int[LUTSize]; |
|
w = _w; |
|
h = _h; |
|
prob = _prob; |
|
|
|
LUT[0] = 1; |
|
int j = 1; |
|
for (int i = 1; i < LUTSize; i++) |
|
{ |
|
LUT[i] = LUTSize + 1; |
|
double ret = nfa(i, j); |
|
if (ret >= 1.0) |
|
{ |
|
while (j < i) |
|
{ |
|
j++; |
|
ret = nfa(i, j); |
|
if (ret <= 1.0) |
|
break; |
|
} |
|
|
|
if (ret >= 1.0) |
|
continue; |
|
} |
|
LUT[i] = j; |
|
} |
|
} |
|
|
|
|
|
NFALUT::~NFALUT() |
|
{ |
|
delete[] LUT; |
|
} |
|
|
|
bool NFALUT::checkValidationByNFA(int n, int k) |
|
{ |
|
if (n >= LUTSize) |
|
return nfa(n, k) <= 1.0; |
|
else |
|
return k >= LUT[n]; |
|
} |
|
|
|
double NFALUT::myAtan2(double yy, double xx) |
|
{ |
|
double angle = fastAtan2((float)yy, (float)xx); |
|
|
|
if (angle > 180) |
|
angle = angle - 180; |
|
|
|
return angle / 180 * CV_PI; |
|
} |
|
|
|
double NFALUT::Comb(double n, double k) //fast combination computation |
|
{ |
|
if (k > n) |
|
return 0; |
|
|
|
double r = 1; |
|
for (double d = 1; d <= k; ++d) |
|
{ |
|
r *= n--; |
|
r /= d; |
|
} |
|
return r; |
|
} |
|
|
|
double NFALUT::nfa(int n, int k) |
|
{ |
|
double sum = 0; |
|
double p = 0.125; |
|
for (int i = k; i <= n; i++) |
|
sum += Comb(n, i) * pow(p, i) * pow(1 - p, n - i); |
|
|
|
return sum * w * w * h * h; |
|
} |
|
|
|
struct StackNode |
|
{ |
|
int r, c; // starting pixel |
|
int parent; // parent chain (-1 if no parent) |
|
int dir; // direction where you are supposed to go |
|
}; |
|
|
|
// Used during Edge Linking |
|
struct Chain |
|
{ |
|
int dir; // Direction of the chain |
|
int len; // # of pixels in the chain |
|
int parent; // Parent of this node (-1 if no parent) |
|
int children[2]; // Children of this node (-1 if no children) |
|
Point *pixels; // Pointer to the beginning of the pixels array |
|
}; |
|
|
|
// light weight struct for Start & End coordinates of the line segment |
|
struct LS |
|
{ |
|
Point2d start; |
|
Point2d end; |
|
|
|
LS(Point2d _start, Point2d _end) |
|
{ |
|
start = _start; |
|
end = _end; |
|
} |
|
}; |
|
|
|
|
|
struct EDLineSegment |
|
{ |
|
double a, b; // y = a + bx (if invert = 0) || x = a + by (if invert = 1) |
|
int invert; |
|
|
|
double sx, sy; // starting x & y coordinates |
|
double ex, ey; // ending x & y coordinates |
|
|
|
int segmentNo; // Edge segment that this line belongs to |
|
int firstPixelIndex; // Index of the first pixel within the segment of pixels |
|
int len; // No of pixels making up the line segment |
|
|
|
EDLineSegment(double _a, double _b, int _invert, double _sx, double _sy, double _ex, double _ey, int _segmentNo, int _firstPixelIndex, int _len) |
|
{ |
|
a = _a; |
|
b = _b; |
|
invert = _invert; |
|
sx = _sx; |
|
sy = _sy; |
|
ex = _ex; |
|
ey = _ey; |
|
segmentNo = _segmentNo; |
|
firstPixelIndex = _firstPixelIndex; |
|
len = _len; |
|
} |
|
}; |
|
|
|
// Circle equation: (x-xc)^2 + (y-yc)^2 = r^2 |
|
struct mCircle { |
|
Point2d center; |
|
double r; |
|
mCircle(Point2d _center, double _r) { center = _center; r = _r; } |
|
}; |
|
|
|
// Ellipse equation: Ax^2 + Bxy + Cy^2 + Dx + Ey + F = 0 |
|
struct mEllipse { |
|
Point2d center; |
|
Size axes; |
|
double theta; |
|
mEllipse(Point2d _center, Size _axes, double _theta) { center = _center; axes = _axes; theta = _theta; } |
|
}; |
|
|
|
//---------------------------------------------------------- |
|
// Ellipse Equation is of the form: |
|
// Ax^2 + Bxy + Cy^2 + Dx + Ey + F = 0 |
|
// |
|
struct EllipseEquation { |
|
double coeff[7]; // coeff[1] = A |
|
|
|
EllipseEquation() { |
|
for (int i = 0; i<7; i++) coeff[i] = 0; |
|
} |
|
|
|
double A() { return coeff[1]; } |
|
double B() { return coeff[2]; } |
|
double C() { return coeff[3]; } |
|
double D() { return coeff[4]; } |
|
double E() { return coeff[5]; } |
|
double F() { return coeff[6]; } |
|
}; |
|
|
|
// ================================ CIRCLES ================================ |
|
struct Circle { |
|
double xc, yc, r; // Center (xc, yc) & radius. |
|
double circleFitError; // circle fit error |
|
double coverRatio; // Percentage of the circle covered by the arcs making up this circle [0-1] |
|
|
|
double *x, *y; // Pointers to buffers containing the pixels making up this circle |
|
int noPixels; // # of pixels making up this circle |
|
|
|
// If this circle is better approximated by an ellipse, we set isEllipse to true & eq contains the ellipse's equation |
|
EllipseEquation eq; |
|
double ellipseFitError; // ellipse fit error |
|
bool isEllipse; |
|
double majorAxisLength; // Length of the major axis |
|
double minorAxisLength; // Length of the minor axis |
|
}; |
|
|
|
// ------------------------------------------- ARCS ---------------------------------------------------- |
|
struct MyArc { |
|
double xc, yc, r; // center x, y and radius |
|
double circleFitError; // Error during circle fit |
|
|
|
double sTheta, eTheta; // Start & end angle in radius |
|
double coverRatio; // Ratio of the pixels covered on the covering circle [0-1] (noPixels/circumference) |
|
|
|
int turn; // Turn direction: 1 or -1 |
|
|
|
int segmentNo; // SegmentNo where this arc belongs |
|
|
|
int sx, sy; // Start (x, y) coordinate |
|
int ex, ey; // End (x, y) coordinate of the arc |
|
|
|
double *x, *y; // Pointer to buffer containing the pixels making up this arc |
|
int noPixels; // # of pixels making up the arc |
|
|
|
bool isEllipse; // Did we fit an ellipse to this arc? |
|
EllipseEquation eq; // If an ellipse, then the ellipse's equation |
|
double ellipseFitError; // Error during ellipse fit |
|
}; |
|
|
|
// =============================== AngleSet ================================== |
|
|
|
// add a circular arc to the list of arcs |
|
inline double ArcLength(double sTheta, double eTheta) |
|
{ |
|
if (eTheta > sTheta) |
|
return eTheta - sTheta; |
|
else |
|
return CV_2PI - sTheta + eTheta; |
|
} |
|
|
|
// A fast implementation of the AngleSet class. The slow implementation is really bad. About 10 times slower than this! |
|
struct AngleSetArc { |
|
double sTheta; |
|
double eTheta; |
|
int next; // Next AngleSetArc in the linked list |
|
}; |
|
|
|
struct AngleSet { |
|
AngleSetArc angles[360]; |
|
int head; |
|
int next; // Next AngleSetArc to be allocated |
|
double overlapAmount; // Total overlap of the arcs in angleSet. Computed during set() function |
|
|
|
AngleSet() { clear(); } |
|
void clear() { head = -1; next = 0; overlapAmount = 0; } |
|
double overlapRatio() { return overlapAmount / CV_2PI; } |
|
|
|
void _set(double sTheta, double eTheta); |
|
void set(double sTheta, double eTheta); |
|
|
|
double _overlap(double sTheta, double eTheta); |
|
double overlap(double sTheta, double eTheta); |
|
|
|
void computeStartEndTheta(double& sTheta, double& eTheta); |
|
double coverRatio(); |
|
}; |
|
|
|
void AngleSet::_set(double sTheta, double eTheta) |
|
{ |
|
int arc = next++; |
|
|
|
angles[arc].sTheta = sTheta; |
|
angles[arc].eTheta = eTheta; |
|
angles[arc].next = -1; |
|
|
|
// Add the current arc to the linked list |
|
int prev = -1; |
|
int current = head; |
|
while (1) |
|
{ |
|
// Empty list? |
|
if (head < 0) |
|
{ |
|
head = arc; |
|
break; |
|
} |
|
|
|
// End of the list. Add to the end |
|
if (current < 0) |
|
{ |
|
CV_Assert(prev >= 0); |
|
angles[prev].next = arc; |
|
break; |
|
} |
|
|
|
if (angles[arc].eTheta <= angles[current].sTheta) |
|
{ |
|
// Add before current |
|
if (prev < 0) |
|
{ |
|
angles[arc].next = current; |
|
head = arc; |
|
} |
|
else |
|
{ |
|
angles[arc].next = current; |
|
angles[prev].next = arc; |
|
} |
|
break; |
|
} |
|
else if (angles[arc].sTheta >= angles[current].eTheta) |
|
{ |
|
// continue |
|
prev = current; |
|
current = angles[current].next; |
|
|
|
// End of the list? |
|
if (current < 0) |
|
{ |
|
angles[prev].next = arc; |
|
break; |
|
} |
|
} |
|
else |
|
{ |
|
// overlaps with current. Join |
|
// First delete current from the list |
|
if (prev < 0) |
|
head = angles[head].next; |
|
else |
|
angles[prev].next = angles[current].next; |
|
|
|
// Update overlap amount. |
|
if (angles[arc].eTheta < angles[current].eTheta) |
|
{ |
|
overlapAmount += angles[arc].eTheta - angles[current].sTheta; |
|
} |
|
else |
|
{ |
|
overlapAmount += angles[current].eTheta - angles[arc].sTheta; |
|
} |
|
|
|
// Now join current with arc |
|
if (angles[current].sTheta < angles[arc].sTheta) |
|
angles[arc].sTheta = angles[current].sTheta; |
|
if (angles[current].eTheta > angles[arc].eTheta) |
|
angles[arc].eTheta = angles[current].eTheta; |
|
current = angles[current].next; |
|
} |
|
} |
|
} |
|
|
|
void AngleSet::set(double sTheta, double eTheta) |
|
{ |
|
if (eTheta > sTheta) |
|
{ |
|
_set(sTheta, eTheta); |
|
} |
|
else |
|
{ |
|
_set(sTheta, CV_2PI); |
|
_set(0, eTheta); |
|
} |
|
} |
|
|
|
double AngleSet::_overlap(double sTheta, double eTheta) |
|
{ |
|
double o = 0; |
|
|
|
int current = head; |
|
while (current >= 0) |
|
{ |
|
if (sTheta > angles[current].eTheta) |
|
{ |
|
current = angles[current].next; |
|
continue; |
|
} |
|
else if (eTheta < angles[current].sTheta) |
|
break; |
|
|
|
// 3 cases. |
|
if (sTheta < angles[current].sTheta && eTheta > angles[current].eTheta) |
|
{ |
|
o += angles[current].eTheta - angles[current].sTheta; |
|
|
|
} |
|
else if (sTheta < angles[current].sTheta) |
|
{ |
|
o += eTheta - angles[current].sTheta; |
|
} |
|
else |
|
{ |
|
o += angles[current].eTheta - sTheta; |
|
} |
|
|
|
current = angles[current].next; |
|
} |
|
return o; |
|
} |
|
|
|
double AngleSet::overlap(double sTheta, double eTheta) |
|
{ |
|
double o; |
|
|
|
if (eTheta > sTheta) |
|
{ |
|
o = _overlap(sTheta, eTheta); |
|
} |
|
else |
|
{ |
|
o = _overlap(sTheta, CV_2PI); |
|
o += _overlap(0, eTheta); |
|
} |
|
return o / ArcLength(sTheta, eTheta); |
|
} |
|
|
|
void AngleSet::computeStartEndTheta(double& sTheta, double& eTheta) |
|
{ |
|
// Special case: Just one arc |
|
if (angles[head].next < 0) |
|
{ |
|
sTheta = angles[head].sTheta; |
|
eTheta = angles[head].eTheta; |
|
|
|
return; |
|
} |
|
|
|
// OK. More than one arc. Find the biggest gap |
|
int current = head; |
|
int nextArc = angles[current].next; |
|
|
|
double biggestGapSTheta = angles[current].eTheta; |
|
double biggestGapEtheta = angles[nextArc].sTheta; |
|
double biggestGapLength = biggestGapEtheta - biggestGapSTheta; |
|
|
|
double start, end, len; |
|
while (1) |
|
{ |
|
current = nextArc; |
|
nextArc = angles[nextArc].next; |
|
if (nextArc < 0) |
|
break; |
|
|
|
start = angles[current].eTheta; |
|
end = angles[nextArc].sTheta; |
|
len = end - start; |
|
|
|
if (len > biggestGapLength) |
|
{ |
|
biggestGapSTheta = start; |
|
biggestGapEtheta = end; |
|
biggestGapLength = len; |
|
} |
|
} |
|
|
|
// Compute the gap between the last arc & the first arc |
|
start = angles[current].eTheta; |
|
end = angles[head].sTheta; |
|
len = CV_2PI - start + end; |
|
if (len > biggestGapLength) |
|
{ |
|
biggestGapSTheta = start; |
|
biggestGapEtheta = end; |
|
} |
|
sTheta = biggestGapEtheta; |
|
eTheta = biggestGapSTheta; |
|
} |
|
|
|
double AngleSet::coverRatio() |
|
{ |
|
int current = head; |
|
|
|
double total = 0; |
|
while (current >= 0) |
|
{ |
|
total += angles[current].eTheta - angles[current].sTheta; |
|
current = angles[current].next; |
|
} |
|
return total / CV_2PI; |
|
} |
|
|
|
struct EDArcs { |
|
MyArc *arcs; |
|
int noArcs; |
|
|
|
public: |
|
EDArcs(int size = 10000) { |
|
arcs = new MyArc[size]; |
|
noArcs = 0; |
|
} |
|
|
|
~EDArcs() { |
|
delete[] arcs; |
|
} |
|
}; |
|
|
|
struct BufferManager { |
|
double *x, *y; |
|
int index; |
|
|
|
BufferManager(int maxSize) { |
|
x = new double[maxSize]; |
|
y = new double[maxSize]; |
|
index = 0; |
|
} |
|
|
|
~BufferManager() { |
|
delete[] x; |
|
delete[] y; |
|
} |
|
|
|
double *getX() { return &x[index]; } |
|
double *getY() { return &y[index]; } |
|
void move(int size) { index += size; } |
|
}; |
|
|
|
struct Info { |
|
int sign; // -1 or 1: sign of the cross product |
|
double angle; // angle with the next line (in radians) |
|
bool taken; // Is this line taken during arc detection |
|
}; |
|
|
|
#endif
|
|
|