EdgeDrawing Improvements 1

pull/2907/head
Suleyman TURKMEN 4 years ago
parent 31c657e037
commit aa850cd83b
  1. 11
      modules/ximgproc/include/opencv2/ximgproc.hpp
  2. 11
      modules/ximgproc/include/opencv2/ximgproc/edge_drawing.hpp
  3. 235
      modules/ximgproc/src/edge_drawing.cpp
  4. 3
      modules/ximgproc/src/edge_drawing_common.hpp

@ -78,6 +78,17 @@ i.e. algorithms which somehow takes into account pixel affinities in natural ima
@defgroup ximgproc_edge_drawing EdgeDrawing
EDGE DRAWING LIBRARY FOR GEOMETRIC FEATURE EXTRACTION AND VALIDATION
Edge Drawing (ED) algorithm is an proactive approach on edge detection problem. In contrast to many other existing edge detection algorithms which follow a subtractive
approach (i.e. after applying gradient filters onto an image eliminating pixels w.r.t. several rules, e.g. non-maximal suppression and hysteresis in Canny), ED algorithm
works via an additive strategy, i.e. it picks edge pixels one by one, hence the name Edge Drawing. Then we process those random shaped edge segments to extract higher level
edge features, i.e. lines, circles, ellipses, etc. The popular method of extraction edge pixels from the thresholded gradient magnitudes is non-maximal supression that tests
every pixel whether it has the maximum gradient response along its gradient direction and eliminates if it does not. However, this method does not check status of the
neighboring pixels, and therefore might result low quality (in terms of edge continuity, smoothness, thinness, localization) edge segments. Instead of non-maximal supression,
ED points a set of edge pixels and join them by maximizing the total gradient response of edge segments. Therefore it can extract high quality edge segments without need for
an additional hysteresis step.
@defgroup ximgproc_fourier Fourier descriptors
@}
*/

@ -16,17 +16,6 @@ namespace ximgproc
//! @{
/** @brief Class implementing the ED (EdgeDrawing) @cite topal2012edge, EDLines @cite akinlar2011edlines, EDPF @cite akinlar2012edpf and EDCircles @cite akinlar2013edcircles algorithms
EDGE DRAWING LIBRARY FOR GEOMETRIC FEATURE EXTRACTION AND VALIDATION
Edge Drawing (ED) algorithm is an proactive approach on edge detection problem. In contrast to many other existing edge detection algorithms which follow a subtractive
approach (i.e. after applying gradient filters onto an image eliminating pixels w.r.t. several rules, e.g. non-maximal suppression and hysteresis in Canny), ED algorithm
works via an additive strategy, i.e. it picks edge pixels one by one, hence the name Edge Drawing. Then we process those random shaped edge segments to extract higher level
edge features, i.e. lines, circles, ellipses, etc. The popular method of extraction edge pixels from the thresholded gradient magnitudes is non-maximal supressiun that tests
every pixel whether it has the maximum gradient response along its gradient direction and eliminates if it does not. However, this method does not check status of the
neighboring pixels, and therefore might result low quality (in terms of edge continuity, smoothness, thinness, localization) edge segments. Instead of non-maximal supression,
ED points a set of edge pixels and join them by maximizing the total gradient response of edge segments. Therefore it can extract high quality edge segments without need for
an additional hysteresis step.
*/
class CV_EXPORTS_W EdgeDrawing : public Algorithm

@ -12,6 +12,86 @@ namespace cv
namespace ximgproc
{
struct ComputeGradientBody : ParallelLoopBody
{
void operator() (const Range& range) const CV_OVERRIDE;
Mat_<uchar> src;
mutable Mat_<ushort> gradImage;
mutable Mat_<uchar> dirImage;
int gradThresh;
int op;
bool SumFlag;
int* grads;
bool PFmode;
};
void ComputeGradientBody::operator() (const Range& range) const
{
const int last_col = src.cols - 1;
int gx = 0;
int gy = 0;
int sum;
for (int y = range.start; y < range.end; ++y)
{
const uchar* srcPrevRow = src[y - 1];
const uchar* srcCurRow = src[y];
const uchar* srcNextRow = src[y + 1];
ushort* gradRow = gradImage[y];
uchar* dirRow = dirImage[y];
for (int x = 1; x < last_col; ++x)
{
int com1 = srcNextRow[x + 1] - srcPrevRow[x - 1];
int com2 = srcPrevRow[x + 1] - srcNextRow[x - 1];
switch (op)
{
case EdgeDrawing::PREWITT:
gx = abs(com1 + com2 + srcCurRow[x + 1] - srcCurRow[x - 1]);
gy = abs(com1 - com2 + srcNextRow[x] - srcPrevRow[x]);
break;
case EdgeDrawing::SOBEL:
gx = abs(com1 + com2 + 2 * (srcCurRow[x + 1] - srcCurRow[x - 1]));
gy = abs(com1 - com2 + 2 * (srcNextRow[x] - srcPrevRow[x]));
break;
case EdgeDrawing::SCHARR:
gx = abs(3 * (com1 + com2) + 10 * (srcCurRow[x + 1] - srcCurRow[x - 1]));
gy = abs(3 * (com1 - com2) + 10 * (srcNextRow[x] - srcPrevRow[x]));
break;
case EdgeDrawing::LSD:
// com1 and com2 differs from previous operators, because LSD has 2x2 kernel
com1 = srcNextRow[x + 1] - srcCurRow[x];
com2 = srcCurRow[x + 1] - srcNextRow[x];
gx = abs(com1 + com2);
gy = abs(com1 - com2);
break;
}
if (SumFlag)
sum = gx + gy;
else
sum = (int)sqrt((double)gx * gx + gy * gy);
gradRow[x] = (ushort)sum;
if (PFmode)
grads[sum]++;
if (sum >= gradThresh)
{
if (gx >= gy)
dirRow[x] = EDGE_VERTICAL;
else
dirRow[x] = EDGE_HORIZONTAL;
}
}
}
}
class EdgeDrawingImpl : public EdgeDrawing
{
public:
@ -23,6 +103,7 @@ public:
};
EdgeDrawingImpl();
~EdgeDrawingImpl();
void detectEdges(InputArray src) CV_OVERRIDE;
void getEdgeImage(OutputArray dst) CV_OVERRIDE;
void getGradientImage(OutputArray dst) CV_OVERRIDE;
@ -47,6 +128,7 @@ protected:
double divForTestSegment;
double* dH;
int* grads;
int np;
private:
@ -64,9 +146,9 @@ private:
Mat edgeImage;
Mat gradImage;
Mat dirImage;
uchar *dirImg; // pointer to direction image data
short *gradImg; // pointer to gradient image data
ushort *gradImg; // pointer to gradient image data
int op; // edge detection operator
int gradThresh; // gradient threshold
@ -251,6 +333,16 @@ void EdgeDrawingImpl::write(cv::FileStorage& fs) const
EdgeDrawingImpl::EdgeDrawingImpl()
{
params = EdgeDrawing::Params();
nfa = new NFALUT(1, 1/2, 1, 1);
dH = new double[MAX_GRAD_VALUE];
grads = new int[MAX_GRAD_VALUE];
}
EdgeDrawingImpl::~EdgeDrawingImpl()
{
delete nfa;
delete[] dH;
delete[] grads;
}
void EdgeDrawingImpl::detectEdges(InputArray src)
@ -259,13 +351,6 @@ void EdgeDrawingImpl::detectEdges(InputArray src)
anchorThresh = params.AnchorThresholdValue;
op = params.EdgeDetectionOperator;
if (params.PFmode)
{
op = PREWITT;
gradThresh = 11;
anchorThresh = 3;
}
// Check parameters for sanity
if (op < 0 || op > 3)
op = 0;
@ -289,7 +374,8 @@ void EdgeDrawingImpl::detectEdges(InputArray src)
width = srcImage.cols;
edgeImage = Mat(height, width, CV_8UC1, Scalar(0)); // initialize edge Image
gradImage = Mat(height, width, CV_16SC1); // gradImage contains short values
gradImage = Mat(height, width, CV_16UC1); // gradImage contains short values
dirImage = Mat(height, width, CV_8UC1);
if (params.Sigma < 1.0)
smoothImage = srcImage;
@ -300,11 +386,15 @@ void EdgeDrawingImpl::detectEdges(InputArray src)
// Assign Pointers from Mat's data
smoothImg = smoothImage.data;
gradImg = (short*)gradImage.data;
gradImg = (ushort*)gradImage.data;
edgeImg = edgeImage.data;
dirImg = dirImage.data;
Mat _dirImg = Mat(height, width, CV_8UC1);
dirImg = _dirImg.data;
if (params.PFmode)
{
memset(dH, 0, sizeof(double) * MAX_GRAD_VALUE);
memset(grads, 0, sizeof(int) * MAX_GRAD_VALUE);
}
ComputeGradient(); // COMPUTE GRADIENT & EDGE DIRECTION MAPS
ComputeAnchorPoints(); // COMPUTE ANCHORS
@ -312,6 +402,15 @@ void EdgeDrawingImpl::detectEdges(InputArray src)
if (params.PFmode)
{
// Compute probability function H
int size = (width - 2) * (height - 2);
for (int i = MAX_GRAD_VALUE - 1; i > 0; i--)
grads[i - 1] += grads[i];
for (int i = 0; i < MAX_GRAD_VALUE; i++)
dH[i] = (double)grads[i] / ((double)size);
divForTestSegment = 2.25; // Some magic number :-)
memset(edgeImg, 0, width * height); // clear edge image
np = 0;
@ -335,14 +434,12 @@ void EdgeDrawingImpl::getEdgeImage(OutputArray _dst)
edgeImage.copyTo(_dst);
}
void EdgeDrawingImpl::getGradientImage(OutputArray _dst)
{
if (!gradImage.empty())
convertScaleAbs(gradImage, _dst);
gradImage.copyTo(_dst);
}
std::vector<std::vector<Point> > EdgeDrawingImpl::getSegments()
{
return segmentPoints;
@ -350,90 +447,27 @@ std::vector<std::vector<Point> > EdgeDrawingImpl::getSegments()
void EdgeDrawingImpl::ComputeGradient()
{
#define MAX_GRAD_VALUE 128*256
dH = new double[MAX_GRAD_VALUE];
memset(dH, 0, sizeof(double) * MAX_GRAD_VALUE);
int* grads = new int[MAX_GRAD_VALUE];
memset(grads, 0, sizeof(int) * MAX_GRAD_VALUE);
// Initialize gradient image for row = 0, row = height-1, column=0, column=width-1
for (int j = 0; j < width; j++)
{
gradImg[j] = gradImg[(height - 1) * width + j] = (short)gradThresh - 1;
gradImg[j] = gradImg[(height - 1) * width + j] = (ushort)gradThresh - 1;
}
for (int i = 1; i < height - 1; i++)
{
gradImg[i * width] = gradImg[(i + 1) * width - 1] = (short)gradThresh - 1;
}
for (int i = 1; i < height - 1; i++)
{
for (int j = 1; j < width - 1; j++)
{
int com1 = smoothImg[(i + 1) * width + j + 1] - smoothImg[(i - 1) * width + j - 1];
int com2 = smoothImg[(i - 1) * width + j + 1] - smoothImg[(i + 1) * width + j - 1];
int gx(0);
int gy(0);
switch (op)
{
case PREWITT:
gx = abs(com1 + com2 + (smoothImg[i * width + j + 1] - smoothImg[i * width + j - 1]));
gy = abs(com1 - com2 + (smoothImg[(i + 1) * width + j] - smoothImg[(i - 1) * width + j]));
break;
case SOBEL:
gx = abs(com1 + com2 + 2 * (smoothImg[i * width + j + 1] - smoothImg[i * width + j - 1]));
gy = abs(com1 - com2 + 2 * (smoothImg[(i + 1) * width + j] - smoothImg[(i - 1) * width + j]));
break;
case SCHARR:
gx = abs(3 * (com1 + com2) + 10 * (smoothImg[i * width + j + 1] - smoothImg[i * width + j - 1]));
gy = abs(3 * (com1 - com2) + 10 * (smoothImg[(i + 1) * width + j] - smoothImg[(i - 1) * width + j]));
break;
case LSD:
// com1 and com2 differs from previous operators, because LSD has 2x2 kernel
com1 = smoothImg[(i + 1) * width + j + 1] - smoothImg[i * width + j];
com2 = smoothImg[i * width + j + 1] - smoothImg[(i + 1) * width + j];
gx = abs(com1 + com2);
gy = abs(com1 - com2);
break;
}
int sum;
if (params.SumFlag)
sum = gx + gy;
else
sum = (int)sqrt((double)gx * gx + gy * gy);
int index = i * width + j;
gradImg[index] = (short)sum;
grads[(int)sum]++;
if (sum >= gradThresh)
{
if (gx >= gy)
dirImg[index] = EDGE_VERTICAL;
else
dirImg[index] = EDGE_HORIZONTAL;
}
}
gradImg[i * width] = gradImg[(i + 1) * width - 1] = (ushort)gradThresh - 1;
}
// Compute probability function H
int size = (width - 2) * (height - 2);
for (int i = MAX_GRAD_VALUE - 1; i > 0; i--)
grads[i - 1] += grads[i];
for (int i = 0; i < MAX_GRAD_VALUE; i++)
dH[i] = (double)grads[i] / ((double)size);
#undef MAX_GRAD_VALUE
ComputeGradientBody body;
body.src = smoothImage;
body.gradImage = gradImage;
body.dirImage = dirImage;
body.gradThresh = gradThresh;
body.SumFlag = params.SumFlag;
body.op = op;
body.grads = grads;
body.PFmode = params.PFmode;
parallel_for_(Range(1, smoothImage.rows - 1), body);
}
void EdgeDrawingImpl::ComputeAnchorPoints()
@ -526,7 +560,6 @@ void EdgeDrawingImpl::JoinAnchorPointsUsingSortedAnchors()
stack[top].c = j;
stack[top].dir = UP;
stack[top].parent = 0;
}
else
{
@ -1273,12 +1306,12 @@ void EdgeDrawingImpl::detectLines(OutputArray _lines)
#define PRECISON_ANGLE 22.5
precision = (PRECISON_ANGLE / 180) * CV_PI;
double prob = 0.125;
#undef PRECISON_ANGLE
if (params.NFAValidation)
if (nfa->LUTSize == 1 && params.NFAValidation)
{
int lutSize = (width + height) / 8;
double prob = 1.0 / 8; // probability of alignment
nfa = new NFALUT(lutSize, prob, width, height);
ValidateLineSegments();
}
@ -2787,7 +2820,6 @@ void EdgeDrawingImpl::GenerateCandidateCircles()
void EdgeDrawingImpl::DetectArcs()
{
double maxLineLengthThreshold = MAX(width, height) / 5;
double MIN_ANGLE = CV_PI / 30; // 6 degrees
@ -3155,8 +3187,6 @@ void EdgeDrawingImpl::DetectArcs()
void EdgeDrawingImpl::ValidateCircles(bool validate)
{
precision = CV_PI / 16; // Alignment precision
double prob = 1.0 / 8; // probability of alignment
double max = width;
if (height > max)
max = height;
@ -3167,8 +3197,12 @@ void EdgeDrawingImpl::ValidateCircles(bool validate)
double* px = new double[8 * (width + height)];
double* py = new double[8 * (width + height)];
int lutSize = (width + height) / 8;
nfa = new NFALUT(lutSize, prob, width, height); // create look up table
if (nfa->LUTSize == 1 && params.NFAValidation)
{
int lutSize = (width + height) / 8;
double prob = 1.0 / 8; // probability of alignment
nfa = new NFALUT(lutSize, prob, width, height); // create look up table
}
// Validate circles & ellipses
bool validateAgain;
@ -3412,7 +3446,6 @@ void EdgeDrawingImpl::ValidateCircles(bool validate)
delete[] px;
delete[] py;
delete nfa;
}
void EdgeDrawingImpl::JoinCircles()

@ -37,6 +37,7 @@
// 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;
@ -64,7 +65,7 @@ private:
NFALUT::NFALUT(int size, double _prob, int _w, int _h)
{
LUTSize = size;
LUTSize = size > 60 ? 60 : size;
LUT = new int[LUTSize];
w = _w;
h = _h;

Loading…
Cancel
Save