Commit 965b3759 authored by Daniel Angelov's avatar Daniel Angelov

Update on the class to reflect the review. Split the class into virtual and…

Update on the class to reflect the review. Split the class into virtual and implementation. change of name to LineSegmentDetector, using Input/Output-Arrays, general clean ups.
parent 694d9ff2
......@@ -835,34 +835,16 @@ protected:
Point2f bottomRight;
};
class CV_EXPORTS_W LSD
class LineSegmentDetector : public Algorithm
{
public:
/**
* Create an LSD object. Specifying scale, number of subdivisions for the image, should the lines be refined and other constants as follows:
*
* @param _refine How should the lines found be refined?
* LSD_REFINE_NONE - No refinement applied.
* LSD_REFINE_STD - Standard refinement is applied. E.g. breaking arches into smaller line approximations.
* LSD_REFINE_ADV - Advanced refinement. Number of false alarms is calculated,
* lines are refined through increase of precision, decrement in size, etc.
* @param _scale The scale of the image that will be used to find the lines. Range (0..1].
* @param _sigma_scale Sigma for Gaussian filter is computed as sigma = _sigma_scale/_scale.
* @param _quant Bound to the quantization error on the gradient norm.
* @param _ang_th Gradient angle tolerance in degrees.
* @param _log_eps Detection threshold: -log10(NFA) > _log_eps
* @param _density_th Minimal density of aligned region points in rectangle.
* @param _n_bins Number of bins in pseudo-ordering of gradient modulus.
*/
LSD(int _refine = LSD_REFINE_STD, double _scale = 0.8,
double _sigma_scale = 0.6, double _quant = 2.0, double _ang_th = 22.5,
double _log_eps = 0, double _density_th = 0.7, int _n_bins = 1024);
/**
* Detect lines in the input image with the specified ROI.
*
* @param _image A grayscale(CV_8UC1) input image.
* If only a roi needs to be selected, use
* lsd_ptr->detect(image(roi), ..., lines);
* lines += Scalar(roi.x, roi.y, roi.x, roi.y);
* @param _lines Return: A vector of Vec4i elements specifying the beginning and ending point of a line.
* Where Vec4i is (x1, y1, x2, y2), point 1 is the start, point 2 - end.
* Returned lines are strictly oriented depending on the gradient.
......@@ -875,10 +857,11 @@ public:
* * -1 corresponds to 10 mean false alarms
* * 0 corresponds to 1 mean false alarm
* * 1 corresponds to 0.1 mean false alarms
* This vector will be calculated _only_ when the objects type is REFINE_ADV
*/
void detect(const cv::InputArray _image, cv::OutputArray _lines, cv::Rect _roi = cv::Rect(),
cv::OutputArray width = cv::noArray(), cv::OutputArray prec = cv::noArray(),
cv::OutputArray nfa = cv::noArray());
virtual void detect(const InputArray _image, OutputArray _lines,
OutputArray width = noArray(), OutputArray prec = noArray(),
OutputArray nfa = noArray()) = 0;
/**
* Draw lines on the given canvas.
......@@ -887,7 +870,7 @@ public:
* Should have the size of the image, where the lines were found
* @param lines The lines that need to be drawn
*/
static void drawSegments(cv::Mat& image, const std::vector<cv::Vec4i>& lines);
virtual void drawSegments(InputOutputArray image, const InputArray lines) = 0;
/**
* Draw both vectors on the image canvas. Uses blue for lines 1 and red for lines 2.
......@@ -898,163 +881,23 @@ public:
* @param lines2 The second lines that need to be drawn. Color - Red.
* @return The number of mismatching pixels between lines1 and lines2.
*/
static int compareSegments(const cv::Size& size, const std::vector<cv::Vec4i>& lines1, const std::vector<cv::Vec4i> lines2, cv::Mat* image = 0);
private:
cv::Mat image;
cv::Mat_<double> scaled_image;
double *scaled_image_data;
cv::Mat_<double> angles; // in rads
double *angles_data;
cv::Mat_<double> modgrad;
double *modgrad_data;
cv::Mat_<uchar> used;
int img_width;
int img_height;
double LOG_NT;
cv::Rect roi;
int roix, roiy;
const double SCALE;
const int doRefine;
const double SIGMA_SCALE;
const double QUANT;
const double ANG_TH;
const double LOG_EPS;
const double DENSITY_TH;
const int N_BINS;
struct RegionPoint {
int x;
int y;
uchar* used;
double angle;
double modgrad;
};
struct coorlist
{
cv::Point2i p;
struct coorlist* next;
};
struct rect
{
double x1, y1, x2, y2; // first and second point of the line segment
double width; // rectangle width
double x, y; // center of the rectangle
double theta; // angle
double dx,dy; // (dx,dy) is vector oriented as the line segment
double prec; // tolerance angle
double p; // probability of a point with angle within 'prec'
};
/**
* Detect lines in the whole input image.
*
* @param lines Return: A vector of Vec4i elements specifying the beginning and ending point of a line.
* Where Vec4i is (x1, y1, x2, y2), point 1 is the start, point 2 - end.
* Returned lines are strictly oriented depending on the gradient.
* @param widths Return: Vector of widths of the regions, where the lines are found. E.g. Width of line.
* @param precisions Return: Vector of precisions with which the lines are found.
* @param nfas Return: Vector containing number of false alarms in the line region, with precision of 10%.
* The bigger the value, logarithmically better the detection.
* * -1 corresponds to 10 mean false alarms
* * 0 corresponds to 1 mean false alarm
* * 1 corresponds to 0.1 mean false alarms
*/
void flsd(std::vector<cv::Vec4i>& lines,
std::vector<double>* widths, std::vector<double>* precisions,
std::vector<double>* nfas);
/**
* Finds the angles and the gradients of the image. Generates a list of pseudo ordered points.
*
* @param threshold The minimum value of the angle that is considered defined, otherwise NOTDEF
* @param n_bins The number of bins with which gradients are ordered by, using bucket sort.
* @param list Return: Vector of coordinate points that are pseudo ordered by magnitude.
* Pixels would be ordered by norm value, up to a precision given by max_grad/n_bins.
*/
void ll_angle(const double& threshold, const unsigned int& n_bins, std::vector<coorlist>& list);
virtual int compareSegments(const Size& size, const InputArray lines1, const InputArray lines2, Mat* image = 0) = 0;
/**
* Grow a region starting from point s with a defined precision,
* returning the containing points size and the angle of the gradients.
*
* @param s Starting point for the region.
* @param reg Return: Vector of points, that are part of the region
* @param reg_size Return: The size of the region.
* @param reg_angle Return: The mean angle of the region.
* @param prec The precision by which each region angle should be aligned to the mean.
*/
void region_grow(const cv::Point2i& s, std::vector<RegionPoint>& reg,
int& reg_size, double& reg_angle, const double& prec);
/**
* Finds the bounding rotated rectangle of a region.
*
* @param reg The region of points, from which the rectangle to be constructed from.
* @param reg_size The number of points in the region.
* @param reg_angle The mean angle of the region.
* @param prec The precision by which points were found.
* @param p Probability of a point with angle within 'prec'.
* @param rec Return: The generated rectangle.
*/
void region2rect(const std::vector<RegionPoint>& reg, const int reg_size, const double reg_angle,
const double prec, const double p, rect& rec) const;
/**
* Compute region's angle as the principal inertia axis of the region.
* @return Regions angle.
*/
double get_theta(const std::vector<RegionPoint>& reg, const int& reg_size, const double& x,
const double& y, const double& reg_angle, const double& prec) const;
/**
* An estimation of the angle tolerance is performed by the standard deviation of the angle at points
* near the region's starting point. Then, a new region is grown starting from the same point, but using the
* estimated angle tolerance. If this fails to produce a rectangle with the right density of region points,
* 'reduce_region_radius' is called to try to satisfy this condition.
*/
bool refine(std::vector<RegionPoint>& reg, int& reg_size, double reg_angle,
const double prec, double p, rect& rec, const double& density_th);
/**
* Reduce the region size, by elimination the points far from the starting point, until that leads to
* rectangle with the right density of region points or to discard the region if too small.
*/
bool reduce_region_radius(std::vector<RegionPoint>& reg, int& reg_size, double reg_angle,
const double prec, double p, rect& rec, double density, const double& density_th);
/**
* Try some rectangles variations to improve NFA value. Only if the rectangle is not meaningful (i.e., log_nfa <= log_eps).
* @return The new NFA value.
*/
double rect_improve(rect& rec) const;
/**
* Calculates the number of correctly aligned points within the rectangle.
* @return The new NFA value.
*/
double rect_nfa(const rect& rec) const;
/**
* Computes the NFA values based on the total number of points, points that agree.
* n, k, p are the binomial parameters.
* @return The new NFA value.
*/
double nfa(const int& n, const int& k, const double& p) const;
/**
* Is the point at place 'address' aligned to angle theta, up to precision 'prec'?
* @return Whether the point is aligned.
*/
bool isAligned(const int& address, const double& theta, const double& prec) const;
~LineSegmentDetector() {};
protected:
LineSegmentDetector() {};
};
//! Returns a pointer to a LineSegmentDetector class.
CV_EXPORTS Ptr<LineSegmentDetector> createLineSegmentDetectorSmrtPtr(
int _refine = LSD_REFINE_STD, double _scale = 0.8,
double _sigma_scale = 0.6, double _quant = 2.0, double _ang_th = 22.5,
double _log_eps = 0, double _density_th = 0.7, int _n_bins = 1024);
CV_EXPORTS LineSegmentDetector* createLineSegmentDetectorPtr(
int _refine = LSD_REFINE_STD, double _scale = 0.8,
double _sigma_scale = 0.6, double _quant = 2.0, double _ang_th = 22.5,
double _log_eps = 0, double _density_th = 0.7, int _n_bins = 1024);
//! returns type (one of KERNEL_*) of 1D or 2D kernel specified by its coefficients.
CV_EXPORTS int getKernelType(InputArray kernel, Point anchor);
......
......@@ -40,11 +40,8 @@
//M*/
#include "precomp.hpp"
#include <vector>
using namespace cv;
/////////////////////////////////////////////////////////////////////////////////////////
// Default LSD parameters
// SIGMA_SCALE 0.6 - Sigma for Gaussian filter is computed as sigma = sigma_scale/scale.
......@@ -54,10 +51,6 @@ using namespace cv;
// DENSITY_TH 0.7 - Minimal density of region points in rectangle.
// N_BINS 1024 - Number of bins in pseudo-ordering of gradient modulus.
// PI
#ifndef M_PI
#define M_PI CV_PI
#endif
#define M_3_2_PI (3 * CV_PI) / 2 // 3/2 pi
#define M_2__PI (2 * CV_PI) // 2 pi
......@@ -72,7 +65,7 @@ using namespace cv;
#define RELATIVE_ERROR_FACTOR 100.0
const double DEG_TO_RADS = M_PI / 180;
const double DEG_TO_RADS = CV_PI / 180;
#define log_gamma(x) ((x)>15.0?log_gamma_windschitl(x):log_gamma_lanczos(x))
......@@ -84,12 +77,14 @@ struct edge
/////////////////////////////////////////////////////////////////////////////////////////
inline double distSq(const double x1, const double y1, const double x2, const double y2)
inline double distSq(const double x1, const double y1,
const double x2, const double y2)
{
return (x2 - x1)*(x2 - x1) + (y2 - y1)*(y2 - y1);
}
inline double dist(const double x1, const double y1, const double x2, const double y2)
inline double dist(const double x1, const double y1,
const double x2, const double y2)
{
return sqrt(distSq(x1, y1, x2, y2));
}
......@@ -163,7 +158,256 @@ inline double log_gamma_lanczos(const double& x)
}
///////////////////////////////////////////////////////////////////////////////////////////////////////////////
LSD::LSD(int _refine, double _scale, double _sigma_scale, double _quant,
namespace cv{
class LineSegmentDetectorImpl : public LineSegmentDetector
{
public:
/**
* Create a LineSegmentDetectorImpl object. Specifying scale, number of subdivisions for the image, should the lines be refined and other constants as follows:
*
* @param _refine How should the lines found be refined?
* LSD_REFINE_NONE - No refinement applied.
* LSD_REFINE_STD - Standard refinement is applied. E.g. breaking arches into smaller line approximations.
* LSD_REFINE_ADV - Advanced refinement. Number of false alarms is calculated,
* lines are refined through increase of precision, decrement in size, etc.
* @param _scale The scale of the image that will be used to find the lines. Range (0..1].
* @param _sigma_scale Sigma for Gaussian filter is computed as sigma = _sigma_scale/_scale.
* @param _quant Bound to the quantization error on the gradient norm.
* @param _ang_th Gradient angle tolerance in degrees.
* @param _log_eps Detection threshold: -log10(NFA) > _log_eps
* @param _density_th Minimal density of aligned region points in rectangle.
* @param _n_bins Number of bins in pseudo-ordering of gradient modulus.
*/
LineSegmentDetectorImpl(int _refine = LSD_REFINE_STD, double _scale = 0.8,
double _sigma_scale = 0.6, double _quant = 2.0, double _ang_th = 22.5,
double _log_eps = 0, double _density_th = 0.7, int _n_bins = 1024);
/**
* Detect lines in the input image with the specified ROI.
*
* @param _image A grayscale(CV_8UC1) input image.
* If only a roi needs to be selected, use
* lsd_ptr->detect(image(roi), ..., lines);
* lines += Scalar(roi.x, roi.y, roi.x, roi.y);
* @param _lines Return: A vector of Vec4i elements specifying the beginning and ending point of a line.
* Where Vec4i is (x1, y1, x2, y2), point 1 is the start, point 2 - end.
* Returned lines are strictly oriented depending on the gradient.
* @param _roi Return: ROI of the image, where lines are to be found. If specified, the returning
* lines coordinates are image wise.
* @param width Return: Vector of widths of the regions, where the lines are found. E.g. Width of line.
* @param prec Return: Vector of precisions with which the lines are found.
* @param nfa Return: Vector containing number of false alarms in the line region, with precision of 10%.
* The bigger the value, logarithmically better the detection.
* * -1 corresponds to 10 mean false alarms
* * 0 corresponds to 1 mean false alarm
* * 1 corresponds to 0.1 mean false alarms
* This vector will be calculated _only_ when the objects type is REFINE_ADV
*/
void detect(const InputArray _image, OutputArray _lines,
OutputArray width = noArray(), OutputArray prec = noArray(),
OutputArray nfa = noArray());
/**
* Draw lines on the given canvas.
*
* @param image The image, where lines will be drawn.
* Should have the size of the image, where the lines were found
* @param lines The lines that need to be drawn
*/
void drawSegments(InputOutputArray image, const InputArray lines);
/**
* Draw both vectors on the image canvas. Uses blue for lines 1 and red for lines 2.
*
* @param image The image, where lines will be drawn.
* Should have the size of the image, where the lines were found
* @param lines1 The first lines that need to be drawn. Color - Blue.
* @param lines2 The second lines that need to be drawn. Color - Red.
* @return The number of mismatching pixels between lines1 and lines2.
*/
int compareSegments(const Size& size, const InputArray lines1, const InputArray lines2, Mat* image = 0);
private:
Mat image;
Mat_<double> scaled_image;
double *scaled_image_data;
Mat_<double> angles; // in rads
double *angles_data;
Mat_<double> modgrad;
double *modgrad_data;
Mat_<uchar> used;
int img_width;
int img_height;
double LOG_NT;
bool w_needed;
bool p_needed;
bool n_needed;
const double SCALE;
const int doRefine;
const double SIGMA_SCALE;
const double QUANT;
const double ANG_TH;
const double LOG_EPS;
const double DENSITY_TH;
const int N_BINS;
struct RegionPoint {
int x;
int y;
uchar* used;
double angle;
double modgrad;
};
struct coorlist
{
Point2i p;
struct coorlist* next;
};
struct rect
{
double x1, y1, x2, y2; // first and second point of the line segment
double width; // rectangle width
double x, y; // center of the rectangle
double theta; // angle
double dx,dy; // (dx,dy) is vector oriented as the line segment
double prec; // tolerance angle
double p; // probability of a point with angle within 'prec'
};
/**
* Detect lines in the whole input image.
*
* @param lines Return: A vector of Vec4i elements specifying the beginning and ending point of a line.
* Where Vec4i is (x1, y1, x2, y2), point 1 is the start, point 2 - end.
* Returned lines are strictly oriented depending on the gradient.
* @param widths Return: Vector of widths of the regions, where the lines are found. E.g. Width of line.
* @param precisions Return: Vector of precisions with which the lines are found.
* @param nfas Return: Vector containing number of false alarms in the line region, with precision of 10%.
* The bigger the value, logarithmically better the detection.
* * -1 corresponds to 10 mean false alarms
* * 0 corresponds to 1 mean false alarm
* * 1 corresponds to 0.1 mean false alarms
*/
void flsd(std::vector<Vec4i>& lines,
std::vector<double>& widths, std::vector<double>& precisions,
std::vector<double>& nfas);
/**
* Finds the angles and the gradients of the image. Generates a list of pseudo ordered points.
*
* @param threshold The minimum value of the angle that is considered defined, otherwise NOTDEF
* @param n_bins The number of bins with which gradients are ordered by, using bucket sort.
* @param list Return: Vector of coordinate points that are pseudo ordered by magnitude.
* Pixels would be ordered by norm value, up to a precision given by max_grad/n_bins.
*/
void ll_angle(const double& threshold, const unsigned int& n_bins, std::vector<coorlist>& list);
/**
* Grow a region starting from point s with a defined precision,
* returning the containing points size and the angle of the gradients.
*
* @param s Starting point for the region.
* @param reg Return: Vector of points, that are part of the region
* @param reg_size Return: The size of the region.
* @param reg_angle Return: The mean angle of the region.
* @param prec The precision by which each region angle should be aligned to the mean.
*/
void region_grow(const Point2i& s, std::vector<RegionPoint>& reg,
int& reg_size, double& reg_angle, const double& prec);
/**
* Finds the bounding rotated rectangle of a region.
*
* @param reg The region of points, from which the rectangle to be constructed from.
* @param reg_size The number of points in the region.
* @param reg_angle The mean angle of the region.
* @param prec The precision by which points were found.
* @param p Probability of a point with angle within 'prec'.
* @param rec Return: The generated rectangle.
*/
void region2rect(const std::vector<RegionPoint>& reg, const int reg_size, const double reg_angle,
const double prec, const double p, rect& rec) const;
/**
* Compute region's angle as the principal inertia axis of the region.
* @return Regions angle.
*/
double get_theta(const std::vector<RegionPoint>& reg, const int& reg_size, const double& x,
const double& y, const double& reg_angle, const double& prec) const;
/**
* An estimation of the angle tolerance is performed by the standard deviation of the angle at points
* near the region's starting point. Then, a new region is grown starting from the same point, but using the
* estimated angle tolerance. If this fails to produce a rectangle with the right density of region points,
* 'reduce_region_radius' is called to try to satisfy this condition.
*/
bool refine(std::vector<RegionPoint>& reg, int& reg_size, double reg_angle,
const double prec, double p, rect& rec, const double& density_th);
/**
* Reduce the region size, by elimination the points far from the starting point, until that leads to
* rectangle with the right density of region points or to discard the region if too small.
*/
bool reduce_region_radius(std::vector<RegionPoint>& reg, int& reg_size, double reg_angle,
const double prec, double p, rect& rec, double density, const double& density_th);
/**
* Try some rectangles variations to improve NFA value. Only if the rectangle is not meaningful (i.e., log_nfa <= log_eps).
* @return The new NFA value.
*/
double rect_improve(rect& rec) const;
/**
* Calculates the number of correctly aligned points within the rectangle.
* @return The new NFA value.
*/
double rect_nfa(const rect& rec) const;
/**
* Computes the NFA values based on the total number of points, points that agree.
* n, k, p are the binomial parameters.
* @return The new NFA value.
*/
double nfa(const int& n, const int& k, const double& p) const;
/**
* Is the point at place 'address' aligned to angle theta, up to precision 'prec'?
* @return Whether the point is aligned.
*/
bool isAligned(const int& address, const double& theta, const double& prec) const;
};
/////////////////////////////////////////////////////////////////////////////////////////
CV_EXPORTS Ptr<LineSegmentDetector> createLineSegmentDetectorSmrtPtr(
int _refine, double _scale, double _sigma_scale, double _quant, double _ang_th,
double _log_eps, double _density_th, int _n_bins)
{
return Ptr<LineSegmentDetector>(new LineSegmentDetectorImpl(
_refine, _scale, _sigma_scale, _quant, _ang_th,
_log_eps, _density_th, _n_bins));
}
CV_EXPORTS LineSegmentDetector* createLineSegmentDetectorPtr(
int _refine, double _scale, double _sigma_scale, double _quant, double _ang_th,
double _log_eps, double _density_th, int _n_bins)
{
return new LineSegmentDetectorImpl(
_refine, _scale, _sigma_scale, _quant, _ang_th,
_log_eps, _density_th, _n_bins);
}
/////////////////////////////////////////////////////////////////////////////////////////
LineSegmentDetectorImpl::LineSegmentDetectorImpl(int _refine, double _scale, double _sigma_scale, double _quant,
double _ang_th, double _log_eps, double _density_th, int _n_bins)
:SCALE(_scale), doRefine(_refine), SIGMA_SCALE(_sigma_scale), QUANT(_quant),
ANG_TH(_ang_th), LOG_EPS(_log_eps), DENSITY_TH(_density_th), N_BINS(_n_bins)
......@@ -173,46 +417,35 @@ LSD::LSD(int _refine, double _scale, double _sigma_scale, double _quant,
_n_bins > 0);
}
void LSD::detect(const cv::InputArray _image, cv::OutputArray _lines, cv::Rect _roi,
cv::OutputArray _width, cv::OutputArray _prec,
cv::OutputArray _nfa)
void LineSegmentDetectorImpl::detect(const InputArray _image, OutputArray _lines,
OutputArray _width, OutputArray _prec, OutputArray _nfa)
{
Mat_<double> img = _image.getMat();
CV_Assert(!img.empty() && img.channels() == 1);
// If default, then convert the whole image, else just the specified by roi
roi = _roi;
if (roi.area() == 0)
{
// Convert image to double
img.convertTo(image, CV_64FC1);
}
else
{
roix = roi.x;
roiy = roi.y;
img(roi).convertTo(image, CV_64FC1);
}
std::vector<Vec4i> lines;
std::vector<double>* w = (_width.needed())?(new std::vector<double>()) : 0;
std::vector<double>* p = (_prec.needed())?(new std::vector<double>()) : 0;
std::vector<double>* n = (_nfa.needed())?(new std::vector<double>()) : 0;
std::vector<double> w, p, n;
w_needed = _width.needed();
p_needed = _prec.needed();
n_needed = _nfa.needed();
CV_Assert((!_nfa.needed()) || // NFA InputArray will be filled _only_ when
(_nfa.needed() && doRefine >= LSD_REFINE_ADV)); // REFINE_ADV type LineSegmentDetectorImpl object is created.
flsd(lines, w, p, n);
Mat(lines).copyTo(_lines);
if(w) Mat(*w).copyTo(_width);
if(p) Mat(*p).copyTo(_prec);
if(n) Mat(*n).copyTo(_nfa);
delete w;
delete p;
delete n;
if(w_needed) Mat(w).copyTo(_width);
if(p_needed) Mat(p).copyTo(_prec);
if(n_needed) Mat(n).copyTo(_nfa);
}
void LSD::flsd(std::vector<Vec4i>& lines,
std::vector<double>* widths, std::vector<double>* precisions,
std::vector<double>* nfas)
void LineSegmentDetectorImpl::flsd(std::vector<Vec4i>& lines,
std::vector<double>& widths, std::vector<double>& precisions,
std::vector<double>& nfas)
{
// Angle tolerance
const double prec = M_PI * ANG_TH / 180;
......@@ -293,19 +526,12 @@ void LSD::flsd(std::vector<Vec4i>& lines,
rec.width /= SCALE;
}
if(roi.area()) // if a roi has been given by the user, adjust coordinates
{
rec.x1 += roix;
rec.y1 += roiy;
rec.x2 += roix;
rec.y2 += roiy;
}
//Store the relevant data
lines.push_back(Vec4i(int(rec.x1), int(rec.y1), int(rec.x2), int(rec.y2)));
if (widths) widths->push_back(rec.width);
if (precisions) precisions->push_back(rec.p);
if (nfas && doRefine >= LSD_REFINE_ADV) nfas->push_back(log_nfa);
if(w_needed) widths.push_back(rec.width);
if(p_needed) precisions.push_back(rec.p);
if(n_needed && doRefine >= LSD_REFINE_ADV) nfas.push_back(log_nfa);
// //Add the linesID to the region on the image
// for(unsigned int el = 0; el < reg_size; el++)
......@@ -316,11 +542,13 @@ void LSD::flsd(std::vector<Vec4i>& lines,
}
}
void LSD::ll_angle(const double& threshold, const unsigned int& n_bins, std::vector<coorlist>& list)
void LineSegmentDetectorImpl::ll_angle(const double& threshold,
const unsigned int& n_bins,
std::vector<coorlist>& list)
{
//Initialize data
angles = cv::Mat_<double>(scaled_image.size());
modgrad = cv::Mat_<double>(scaled_image.size());
angles = Mat_<double>(scaled_image.size());
modgrad = Mat_<double>(scaled_image.size());
angles_data = angles.ptr<double>(0);
modgrad_data = modgrad.ptr<double>(0);
......@@ -357,7 +585,7 @@ void LSD::ll_angle(const double& threshold, const unsigned int& n_bins, std::vec
}
else
{
angles_data[addr] = cv::fastAtan2(float(gx), float(-gy)) * DEG_TO_RADS; // gradient angle computation
angles_data[addr] = fastAtan2(float(gx), float(-gy)) * DEG_TO_RADS; // gradient angle computation
if (norm > max_grad) { max_grad = norm; }
}
......@@ -389,7 +617,7 @@ void LSD::ll_angle(const double& threshold, const unsigned int& n_bins, std::vec
range_e[i] = &list[count];
++count;
}
range_e[i]->p = cv::Point(x, y);
range_e[i]->p = Point(x, y);
range_e[i]->next = 0;
}
}
......@@ -413,7 +641,7 @@ void LSD::ll_angle(const double& threshold, const unsigned int& n_bins, std::vec
}
}
void LSD::region_grow(const cv::Point2i& s, std::vector<RegionPoint>& reg,
void LineSegmentDetectorImpl::region_grow(const Point2i& s, std::vector<RegionPoint>& reg,
int& reg_size, double& reg_angle, const double& prec)
{
// Point to this region
......@@ -459,15 +687,15 @@ void LSD::region_grow(const cv::Point2i& s, std::vector<RegionPoint>& reg,
sumdx += cos(float(angle));
sumdy += sin(float(angle));
// reg_angle is used in the isAligned, so it needs to be updates?
reg_angle = cv::fastAtan2(sumdy, sumdx) * DEG_TO_RADS;
reg_angle = fastAtan2(sumdy, sumdx) * DEG_TO_RADS;
}
}
}
}
}
void LSD::region2rect(const std::vector<RegionPoint>& reg, const int reg_size, const double reg_angle,
const double prec, const double p, rect& rec) const
void LineSegmentDetectorImpl::region2rect(const std::vector<RegionPoint>& reg, const int reg_size,
const double reg_angle, const double prec, const double p, rect& rec) const
{
double x = 0, y = 0, sum = 0;
for(int i = 0; i < reg_size; ++i)
......@@ -524,7 +752,7 @@ void LSD::region2rect(const std::vector<RegionPoint>& reg, const int reg_size, c
if(rec.width < 1.0) rec.width = 1.0;
}
double LSD::get_theta(const std::vector<RegionPoint>& reg, const int& reg_size, const double& x,
double LineSegmentDetectorImpl::get_theta(const std::vector<RegionPoint>& reg, const int& reg_size, const double& x,
const double& y, const double& reg_angle, const double& prec) const
{
double Ixx = 0.0;
......@@ -552,8 +780,8 @@ double LSD::get_theta(const std::vector<RegionPoint>& reg, const int& reg_size,
// Compute angle
double theta = (fabs(Ixx)>fabs(Iyy))?
double(cv::fastAtan2(float(lambda - Ixx), float(Ixy))):
double(cv::fastAtan2(float(Ixy), float(lambda - Iyy))); // in degs
double(fastAtan2(float(lambda - Ixx), float(Ixy))):
double(fastAtan2(float(Ixy), float(lambda - Iyy))); // in degs
theta *= DEG_TO_RADS;
// Correct angle by 180 deg if necessary
......@@ -562,7 +790,7 @@ double LSD::get_theta(const std::vector<RegionPoint>& reg, const int& reg_size,
return theta;
}
bool LSD::refine(std::vector<RegionPoint>& reg, int& reg_size, double reg_angle,
bool LineSegmentDetectorImpl::refine(std::vector<RegionPoint>& reg, int& reg_size, double reg_angle,
const double prec, double p, rect& rec, const double& density_th)
{
double density = double(reg_size) / (dist(rec.x1, rec.y1, rec.x2, rec.y2) * rec.width);
......@@ -610,7 +838,7 @@ bool LSD::refine(std::vector<RegionPoint>& reg, int& reg_size, double reg_angle,
}
}
bool LSD::reduce_region_radius(std::vector<RegionPoint>& reg, int& reg_size, double reg_angle,
bool LineSegmentDetectorImpl::reduce_region_radius(std::vector<RegionPoint>& reg, int& reg_size, double reg_angle,
const double prec, double p, rect& rec, double density, const double& density_th)
{
// Compute region's radius
......@@ -642,13 +870,14 @@ bool LSD::reduce_region_radius(std::vector<RegionPoint>& reg, int& reg_size, dou
region2rect(reg, reg_size ,reg_angle, prec, p, rec);
// Re-compute region points density
density = double(reg_size) / (dist(rec.x1, rec.y1, rec.x2, rec.y2) * rec.width);
density = double(reg_size) /
(dist(rec.x1, rec.y1, rec.x2, rec.y2) * rec.width);
}
return true;
}
double LSD::rect_improve(rect& rec) const
double LineSegmentDetectorImpl::rect_improve(rect& rec) const
{
double delta = 0.5;
double delta_2 = delta / 2.0;
......@@ -752,7 +981,7 @@ double LSD::rect_improve(rect& rec) const
return log_nfa;
}
double LSD::rect_nfa(const rect& rec) const
double LineSegmentDetectorImpl::rect_nfa(const rect& rec) const
{
int total_pts = 0, alg_pts = 0;
double half_width = rec.width / 2.0;
......@@ -871,7 +1100,7 @@ double LSD::rect_nfa(const rect& rec) const
return nfa(total_pts, alg_pts, rec.p);
}
double LSD::nfa(const int& n, const int& k, const double& p) const
double LineSegmentDetectorImpl::nfa(const int& n, const int& k, const double& p) const
{
// Trivial cases
if(n == 0 || k == 0) { return -LOG_NT; }
......@@ -909,7 +1138,7 @@ double LSD::nfa(const int& n, const int& k, const double& p) const
return -log10(bin_tail) - LOG_NT;
}
inline bool LSD::isAligned(const int& address, const double& theta, const double& prec) const
inline bool LineSegmentDetectorImpl::isAligned(const int& address, const double& theta, const double& prec) const
{
if(address < 0) { return false; }
const double& a = angles_data[address];
......@@ -928,18 +1157,18 @@ inline bool LSD::isAligned(const int& address, const double& theta, const double
}
void LSD::drawSegments(cv::Mat& image, const std::vector<cv::Vec4i>& lines)
void LineSegmentDetectorImpl::drawSegments(InputOutputArray _image, const InputArray lines)
{
CV_Assert(!image.empty() && (image.channels() == 1 || image.channels() == 3));
CV_Assert(!_image.empty() && (_image.channels() == 1 || _image.channels() == 3));
Mat gray;
if (image.channels() == 1)
if (_image.channels() == 1)
{
gray = image;
gray = _image.getMatRef();
}
else if (image.channels() == 3)
else if (_image.channels() == 3)
{
cv::cvtColor(image, gray, CV_BGR2GRAY);
cvtColor(_image, gray, CV_BGR2GRAY);
}
// Create a 3 channel image in order to draw colored lines
......@@ -948,38 +1177,47 @@ void LSD::drawSegments(cv::Mat& image, const std::vector<cv::Vec4i>& lines)
planes.push_back(gray);
planes.push_back(gray);
merge(planes, image);
merge(planes, _image);
Mat _lines;
_lines = lines.getMat();
// Draw segments
for(unsigned int i = 0; i < lines.size(); ++i)
for(int i = 0; i < _lines.size().width; ++i)
{
Point b(lines[i][0], lines[i][1]);
Point e(lines[i][2], lines[i][3]);
line(image, b, e, Scalar(0, 0, 255), 1);
const Vec4i& v = _lines.at<Vec4i>(i);
Point b(v[0], v[1]);
Point e(v[2], v[3]);
line(_image.getMatRef(), b, e, Scalar(0, 0, 255), 1);
}
}
int LSD::compareSegments(const cv::Size& size, const std::vector<cv::Vec4i>& lines1, const std::vector<cv::Vec4i> lines2, cv::Mat* image)
int LineSegmentDetectorImpl::compareSegments(const Size& size, const InputArray lines1, const InputArray lines2, Mat* _image)
{
Size sz = size;
if (image && image->size() != size) sz = image->size();
if (_image && _image->size() != size) sz = _image->size();
CV_Assert(sz.area());
Mat_<uchar> I1 = Mat_<uchar>::zeros(sz);
Mat_<uchar> I2 = Mat_<uchar>::zeros(sz);
Mat _lines1;
Mat _lines2;
_lines1 = lines1.getMat();
_lines2 = lines2.getMat();
// Draw segments
for(unsigned int i = 0; i < lines1.size(); ++i)
std::vector<Mat> _lines;
for(int i = 0; i < _lines1.size().width; ++i)
{
Point b(lines1[i][0], lines1[i][1]);
Point e(lines1[i][2], lines1[i][3]);
Point b(_lines1.at<Vec4i>(i)[0], _lines1.at<Vec4i>(i)[1]);
Point e(_lines1.at<Vec4i>(i)[2], _lines1.at<Vec4i>(i)[3]);
line(I1, b, e, Scalar::all(255), 1);
}
for(unsigned int i = 0; i < lines2.size(); ++i)
for(int i = 0; i < _lines2.size().width; ++i)
{
Point b(lines2[i][0], lines2[i][1]);
Point e(lines2[i][2], lines2[i][3]);
Point b(_lines2.at<Vec4i>(i)[0], _lines2.at<Vec4i>(i)[1]);
Point e(_lines2.at<Vec4i>(i)[2], _lines2.at<Vec4i>(i)[3]);
line(I2, b, e, Scalar::all(255), 1);
}
......@@ -988,14 +1226,14 @@ int LSD::compareSegments(const cv::Size& size, const std::vector<cv::Vec4i>& lin
bitwise_xor(I1, I2, Ixor);
int N = countNonZero(Ixor);
if (image)
if (_image)
{
Mat Ig;
if (image->channels() == 1)
if (_image->channels() == 1)
{
cv::cvtColor(*image, *image, CV_GRAY2BGR);
cvtColor(*_image, *_image, CV_GRAY2BGR);
}
CV_Assert(image->isContinuous() && I1.isContinuous() && I2.isContinuous());
CV_Assert(_image->isContinuous() && I1.isContinuous() && I2.isContinuous());
for (unsigned int i = 0; i < I1.total(); ++i)
{
......@@ -1003,14 +1241,16 @@ int LSD::compareSegments(const cv::Size& size, const std::vector<cv::Vec4i>& lin
uchar i2 = I2.data[i];
if (i1 || i2)
{
image->data[3*i + 1] = 0;
if (i1) image->data[3*i] = 255;
else image->data[3*i] = 0;
if (i2) image->data[3*i + 2] = 255;
else image->data[3*i + 2] = 0;
_image->data[3*i + 1] = 0;
if (i1) _image->data[3*i] = 255;
else _image->data[3*i] = 0;
if (i2) _image->data[3*i + 2] = 255;
else _image->data[3*i + 2] = 0;
}
}
}
return N;
}
} // namespace cv
......@@ -23,26 +23,26 @@ protected:
virtual void SetUp();
};
class LSD_ADV: public LSDBase
class Imgproc_LSD_ADV: public LSDBase
{
public:
LSD_ADV() {};
Imgproc_LSD_ADV() {};
protected:
};
class LSD_STD: public LSDBase
class Imgproc_LSD_STD: public LSDBase
{
public:
LSD_STD() {};
Imgproc_LSD_STD() {};
protected:
};
class LSD_NONE: public LSDBase
class Imgproc_LSD_NONE: public LSDBase
{
public:
LSD_NONE() {};
Imgproc_LSD_NONE() {};
protected:
};
......@@ -92,7 +92,7 @@ void LSDBase::GenerateRotatedRect(Mat& image)
rRect.points(vertices);
for (int i = 0; i < 4; i++)
{
line(image, vertices[i], vertices[(i + 1) % 4], Scalar(255));
line(image, vertices[i], vertices[(i + 1) % 4], Scalar(255), 3);
}
}
......@@ -103,110 +103,113 @@ void LSDBase::SetUp()
}
TEST_F(LSD_ADV, whiteNoise)
TEST_F(Imgproc_LSD_ADV, whiteNoise)
{
GenerateWhiteNoise(test_image);
LSD detector(LSD_REFINE_ADV);
detector.detect(test_image, lines);
LineSegmentDetector* detector = createLineSegmentDetectorPtr(LSD_REFINE_ADV);
detector->detect(test_image, lines);
ASSERT_GE((unsigned int)(40), lines.size());
}
TEST_F(LSD_ADV, constColor)
TEST_F(Imgproc_LSD_ADV, constColor)
{
GenerateConstColor(test_image);
LSD detector(LSD_REFINE_ADV);
detector.detect(test_image, lines);
LineSegmentDetector* detector = createLineSegmentDetectorPtr(LSD_REFINE_ADV);
detector->detect(test_image, lines);
ASSERT_EQ((unsigned int)(0), lines.size());
}
TEST_F(LSD_ADV, lines)
TEST_F(Imgproc_LSD_ADV, lines)
{
const unsigned int numOfLines = 3;
GenerateLines(test_image, numOfLines);
LSD detector(LSD_REFINE_ADV);
detector.detect(test_image, lines);
LineSegmentDetector* detector = createLineSegmentDetectorPtr(LSD_REFINE_ADV);
detector->detect(test_image, lines);
ASSERT_EQ(numOfLines * 2, lines.size()); // * 2 because of Gibbs effect
}
TEST_F(LSD_ADV, rotatedRect)
TEST_F(Imgproc_LSD_ADV, rotatedRect)
{
GenerateRotatedRect(test_image);
LSD detector(LSD_REFINE_ADV);
detector.detect(test_image, lines);
ASSERT_LE((unsigned int)(4), lines.size());
LineSegmentDetector* detector = createLineSegmentDetectorPtr(LSD_REFINE_ADV);
detector->detect(test_image, lines);
ASSERT_LE((unsigned int)(2), lines.size());
}
TEST_F(LSD_STD, whiteNoise)
TEST_F(Imgproc_LSD_STD, whiteNoise)
{
GenerateWhiteNoise(test_image);
LSD detector(LSD_REFINE_STD);
detector.detect(test_image, lines);
LineSegmentDetector* detector = createLineSegmentDetectorPtr(LSD_REFINE_STD);
detector->detect(test_image, lines);
ASSERT_GE((unsigned int)(50), lines.size());
}
TEST_F(LSD_STD, constColor)
TEST_F(Imgproc_LSD_STD, constColor)
{
GenerateConstColor(test_image);
LSD detector(LSD_REFINE_STD);
detector.detect(test_image, lines);
LineSegmentDetector* detector = createLineSegmentDetectorPtr(LSD_REFINE_STD);
detector->detect(test_image, lines);
ASSERT_EQ((unsigned int)(0), lines.size());
}
TEST_F(LSD_STD, lines)
TEST_F(Imgproc_LSD_STD, lines)
{
const unsigned int numOfLines = 3; //1
GenerateLines(test_image, numOfLines);
LSD detector(LSD_REFINE_STD);
detector.detect(test_image, lines);
LineSegmentDetector* detector = createLineSegmentDetectorPtr(LSD_REFINE_STD);
detector->detect(test_image, lines);
ASSERT_EQ(numOfLines * 2, lines.size()); // * 2 because of Gibbs effect
}
TEST_F(LSD_STD, rotatedRect)
TEST_F(Imgproc_LSD_STD, rotatedRect)
{
GenerateRotatedRect(test_image);
LSD detector(LSD_REFINE_STD);
detector.detect(test_image, lines);
ASSERT_EQ((unsigned int)(8), lines.size());
LineSegmentDetector* detector = createLineSegmentDetectorPtr(LSD_REFINE_STD);
detector->detect(test_image, lines);
ASSERT_LE((unsigned int)(4), lines.size());
}
TEST_F(LSD_NONE, whiteNoise)
TEST_F(Imgproc_LSD_NONE, whiteNoise)
{
GenerateWhiteNoise(test_image);
LSD detector(LSD_REFINE_NONE);
detector.detect(test_image, lines);
LineSegmentDetector* detector = createLineSegmentDetectorPtr(LSD_REFINE_STD);
detector->detect(test_image, lines);
ASSERT_GE((unsigned int)(50), lines.size());
}
TEST_F(LSD_NONE, constColor)
TEST_F(Imgproc_LSD_NONE, constColor)
{
GenerateConstColor(test_image);
LSD detector(LSD_REFINE_NONE);
detector.detect(test_image, lines);
LineSegmentDetector* detector = createLineSegmentDetectorPtr(LSD_REFINE_NONE);
detector->detect(test_image, lines);
ASSERT_EQ((unsigned int)(0), lines.size());
}
TEST_F(LSD_NONE, lines)
TEST_F(Imgproc_LSD_NONE, lines)
{
const unsigned int numOfLines = 3; //1
GenerateLines(test_image, numOfLines);
LSD detector(LSD_REFINE_NONE);
detector.detect(test_image, lines);
LineSegmentDetector* detector = createLineSegmentDetectorPtr(LSD_REFINE_NONE);
detector->detect(test_image, lines);
ASSERT_EQ(numOfLines * 2, lines.size()); // * 2 because of Gibbs effect
}
TEST_F(LSD_NONE, rotatedRect)
TEST_F(Imgproc_LSD_NONE, rotatedRect)
{
GenerateRotatedRect(test_image);
LSD detector(LSD_REFINE_NONE);
detector.detect(test_image, lines);
ASSERT_EQ((unsigned int)(8), lines.size());
LineSegmentDetector* detector = createLineSegmentDetectorPtr(LSD_REFINE_NONE);
detector->detect(test_image, lines);
ASSERT_LE((unsigned int)(8), lines.size());
}
......@@ -22,28 +22,28 @@ int main(int argc, char** argv)
Mat image = imread(in, IMREAD_GRAYSCALE);
// Create and LSD detector with std refinement.
LSD lsd_std(LSD_REFINE_STD);
LineSegmentDetector* lsd_std = createLineSegmentDetectorPtr(LSD_REFINE_STD);
double start = double(getTickCount());
vector<Vec4i> lines_std;
lsd_std.detect(image, lines_std);
lsd_std->detect(image, lines_std);
double duration_ms = (double(getTickCount()) - start) * 1000 / getTickFrequency();
std::cout << "OpenCV STD (blue) - " << duration_ms << " ms." << std::endl;
// Create an LSD detector with no refinement applied.
LSD lsd_none(LSD_REFINE_NONE);
LineSegmentDetector* lsd_none = createLineSegmentDetectorPtr(LSD_REFINE_NONE);
start = double(getTickCount());
vector<Vec4i> lines_none;
lsd_none.detect(image, lines_none);
lsd_none->detect(image, lines_none);
duration_ms = (double(getTickCount()) - start) * 1000 / getTickFrequency();
std::cout << "OpenCV NONE (red)- " << duration_ms << " ms." << std::endl;
std::cout << "Overlapping pixels are shown in purple." << std::endl;
Mat difference = Mat::zeros(image.size(), CV_8UC1);
LSD::compareSegments(image.size(), lines_std, lines_none, &difference);
lsd_none->compareSegments(image.size(), lines_std, lines_none, &difference);
imshow("Line difference", difference);
Mat drawnLines(image);
LSD::drawSegments(drawnLines, lines_std);
lsd_none->drawSegments(drawnLines, lines_std);
imshow("Standard refinement", drawnLines);
waitKey();
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment