Commit a2582d43 authored by Alexander Alekhin's avatar Alexander Alekhin

Merge pull request #892 from spacetrain:fast_line_detector

parents fa8839bb a7dada15
......@@ -47,6 +47,15 @@
publisher={Springer}
}
@inproceedings{Lee14,
title={Outdoor place recognition in urban environments using straight lines},
author={Lee, Jin Han and Lee, Sehyung and Zhang, Guoxuan and Lim, Jongwoo and Chung, Wan Kyun and Suh, Il Hong},
booktitle={2014 IEEE International Conference on Robotics and Automation (ICRA)},
pages={5550--5557},
year={2014},
organization={IEEE}
}
@inproceedings{Lim2013,
title={Sketch tokens: A learned mid-level representation for contour and object detection},
author={Lim, Joseph J and Zitnick, C Lawrence and Doll{\'a}r, Piotr},
......
......@@ -2,26 +2,26 @@
* By downloading, copying, installing or using the software you agree to this license.
* If you do not agree to this license, do not download, install,
* copy or use the software.
*
*
*
*
* License Agreement
* For Open Source Computer Vision Library
* (3 - clause BSD License)
*
*
* Redistribution and use in source and binary forms, with or without modification,
* are permitted provided that the following conditions are met :
*
*
* *Redistributions of source code must retain the above copyright notice,
* this list of conditions and the following disclaimer.
*
*
* * Redistributions in binary form must reproduce the above copyright notice,
* this list of conditions and the following disclaimer in the documentation
* and / or other materials provided with the distribution.
*
*
* * Neither the names of the copyright holders nor the names of the contributors
* may be used to endorse or promote products derived from this software
* without specific prior written permission.
*
*
* This software is provided by the copyright holders and contributors "as is" and
* any express or implied warranties, including, but not limited to, the implied
* warranties of merchantability and fitness for a particular purpose are disclaimed.
......@@ -49,6 +49,7 @@
#include "ximgproc/slic.hpp"
#include "ximgproc/lsc.hpp"
#include "ximgproc/paillou_filter.hpp"
#include "ximgproc/fast_line_detector.hpp"
/** @defgroup ximgproc Extended Image Processing
......
// 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_FAST_LINE_DETECTOR_HPP__
#define __OPENCV_FAST_LINE_DETECTOR_HPP__
#include <opencv2/core.hpp>
namespace cv
{
namespace ximgproc
{
//! @addtogroup ximgproc_feature
//! @{
/** @brief Class implementing the FLD (Fast Line Detector) algorithm described
in @cite Lee14 .
*/
//! @include samples/fld_lines.cpp
class CV_EXPORTS_W FastLineDetector : public Algorithm
{
public:
/** @example fld_lines.cpp
An example using the FastLineDetector
*/
/** @brief Finds lines in the input image.
This is the output of the default parameters of the algorithm on the above
shown image.
![image](pics/corridor_fld.jpg)
@param _image A grayscale (CV_8UC1) input image. If only a roi needs to be
selected, use: `fld_ptr-\>detect(image(roi), lines, ...);
lines += Scalar(roi.x, roi.y, roi.x, roi.y);`
@param _lines A vector of Vec4f elements specifying the beginning
and ending point of a line. Where Vec4f is (x1, y1, x2, y2), point
1 is the start, point 2 - end. Returned lines are directed so that the
brighter side is on their left.
*/
CV_WRAP virtual void detect(InputArray _image, OutputArray _lines) = 0;
/** @brief Draws the line segments on a given image.
@param _image The image, where the lines will be drawn. Should be bigger
or equal to the image, where the lines were found.
@param lines A vector of the lines that needed to be drawn.
@param draw_arrow If true, arrow heads will be drawn.
*/
CV_WRAP virtual void drawSegments(InputOutputArray _image, InputArray lines,
bool draw_arrow = false) = 0;
virtual ~FastLineDetector() { }
};
/** @brief Creates a smart pointer to a FastLineDetector object and initializes it
@param _length_threshold 10 - Segment shorter than this will be discarded
@param _distance_threshold 1.41421356 - A point placed from a hypothesis line
segment farther than this will be
regarded as an outlier
@param _canny_th1 50 - First threshold for
hysteresis procedure in Canny()
@param _canny_th2 50 - Second threshold for
hysteresis procedure in Canny()
@param _canny_aperture_size 3 - Aperturesize for the sobel
operator in Canny()
@param _do_merge false - If true, incremental merging of segments
will be perfomred
*/
CV_EXPORTS_W Ptr<FastLineDetector> createFastLineDetector(
int _length_threshold = 10, float _distance_threshold = 1.414213562f,
double _canny_th1 = 50.0, double _canny_th2 = 50.0, int _canny_aperture_size = 3,
bool _do_merge = false);
//! @} ximgproc_feature
}
}
#endif
#include <iostream>
#include "opencv2/imgproc.hpp"
#include "opencv2/ximgproc.hpp"
#include "opencv2/imgcodecs.hpp"
#include "opencv2/highgui.hpp"
using namespace std;
using namespace cv;
using namespace cv::ximgproc;
int main(int argc, char** argv)
{
std::string in;
cv::CommandLineParser parser(argc, argv, "{@input|../samples/data/corridor.jpg|input image}{help h||show help message}");
if (parser.has("help"))
{
parser.printMessage();
return 0;
}
in = parser.get<string>("@input");
Mat image = imread(in, IMREAD_GRAYSCALE);
if( image.empty() )
{
return -1;
}
// Create LSD detector
Ptr<LineSegmentDetector> lsd = createLineSegmentDetector();
vector<Vec4f> lines_lsd;
// Create FLD detector
// Param Default value Description
// length_threshold 10 - Segments shorter than this will be discarded
// distance_threshold 1.41421356 - A point placed from a hypothesis line
// segment farther than this will be
// regarded as an outlier
// canny_th1 50 - First threshold for
// hysteresis procedure in Canny()
// canny_th2 50 - Second threshold for
// hysteresis procedure in Canny()
// canny_aperture_size 3 - Aperturesize for the sobel
// operator in Canny()
// do_merge false - If true, incremental merging of segments
// will be perfomred
int length_threshold = 10;
float distance_threshold = 1.41421356f;
double canny_th1 = 50.0;
double canny_th2 = 50.0;
int canny_aperture_size = 3;
bool do_merge = false;
Ptr<FastLineDetector> fld = createFastLineDetector(length_threshold,
distance_threshold, canny_th1, canny_th2, canny_aperture_size,
do_merge);
vector<Vec4f> lines_fld;
// Because of some CPU's power strategy, it seems that the first running of
// an algorithm takes much longer. So here we run both of the algorithmes 10
// times to see each algorithm's processing time with sufficiently warmed-up
// CPU performance.
for(int run_count = 0; run_count < 10; run_count++) {
lines_lsd.clear();
int64 start_lsd = getTickCount();
lsd->detect(image, lines_lsd);
// Detect the lines with LSD
double freq = getTickFrequency();
double duration_ms_lsd = double(getTickCount() - start_lsd) * 1000 / freq;
std::cout << "Elapsed time for LSD: " << duration_ms_lsd << " ms." << std::endl;
lines_fld.clear();
int64 start = getTickCount();
// Detect the lines with FLD
fld->detect(image, lines_fld);
double duration_ms = double(getTickCount() - start) * 1000 / freq;
std::cout << "Ealpsed time for FLD " << duration_ms << " ms." << std::endl;
}
// Show found lines with LSD
Mat line_image_lsd(image);
lsd->drawSegments(line_image_lsd, lines_lsd);
imshow("LSD result", line_image_lsd);
// Show found lines with FLD
Mat line_image_fld(image);
fld->drawSegments(line_image_fld, lines_fld);
imshow("FLD result", line_image_fld);
waitKey();
return 0;
}
// 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.
#include "precomp.hpp"
#include <vector>
#include <iostream>
struct SEGMENT
{
float x1, y1, x2, y2, angle;
};
/////////////////////////////////////////////////////////////////////////////////////////
/////////////////////////////////////////////////////////////////////////////////////////
namespace cv{
namespace ximgproc{
class FastLineDetectorImpl : public FastLineDetector
{
public:
/**
* @param _length_threshold 10 - Segment shorter than this will be discarded
* @param _distance_threshold 1.41421356 - A point placed from a hypothesis line segment
* farther than this will be regarded as an outlier
* @param _canny_th1 50 - First threshold for
* _ hysteresis procedure in Canny()
* @param _canny_th2 50 - Second threshold for
* _ hysteresis procedure in Canny()
* @param _canny_aperture_size 3 - Aperturesize for the sobel
* _ operator in Canny()
* @param _do_merge false - If true, incremental merging of segments
will be perfomred
*/
FastLineDetectorImpl(int _length_threshold = 10, float _distance_threshold = 1.414213562f,
double _canny_th1 = 50.0, double _canny_th2 = 50.0, int _canny_aperture_size = 3,
bool _do_merge = false);
/**
* Detect lines in the input image.
*
* @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 Vec4f elements specifying the beginning and ending point of
* a line. Where Vec4f is (x1, y1, x2, y2), point 1 is the start, point 2 is the end.
* Returned lines are directed so that the brighter side is placed on left.
*/
void detect(InputArray _image, OutputArray _lines);
/**
* 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
* @param draw_arrow If true, arrow heads will be drawn
*/
void drawSegments(InputOutputArray _image, InputArray lines, bool draw_arrow = false);
private:
int imagewidth, imageheight, threshold_length;
float threshold_dist;
double canny_th1, canny_th2;
int canny_aperture_size;
bool do_merge;
FastLineDetectorImpl& operator= (const FastLineDetectorImpl&); // to quiet MSVC
template<class T>
void incidentPoint(const Mat& l, T& pt);
void mergeLines(const SEGMENT& seg1, const SEGMENT& seg2, SEGMENT& seg_merged);
bool mergeSegments(const SEGMENT& seg1, const SEGMENT& seg2, SEGMENT& seg_merged);
bool getPointChain(const Mat& img, Point pt, Point& chained_pt, float& direction, int step);
double distPointLine(const Mat& p, Mat& l);
void extractSegments(const std::vector<Point2i>& points, std::vector<SEGMENT>& segments );
void lineDetection(const Mat& src, std::vector<SEGMENT>& segments_all);
void pointInboardTest(const Mat& src, Point2i& pt);
inline void getAngle(SEGMENT& seg);
void additionalOperationsOnSegment(const Mat& src, SEGMENT& seg);
void drawSegment(Mat& mat, const SEGMENT& seg, Scalar bgr = Scalar(0,255,0),
int thickness = 1, bool directed = true);
};
/////////////////////////////////////////////////////////////////////////////////////////
CV_EXPORTS Ptr<FastLineDetector> createFastLineDetector(
int _length_threshold, float _distance_threshold,
double _canny_th1, double _canny_th2, int _canny_aperture_size, bool _do_merge)
{
return makePtr<FastLineDetectorImpl>(
_length_threshold, _distance_threshold,
_canny_th1, _canny_th2, _canny_aperture_size, _do_merge);
}
/////////////////////////////////////////////////////////////////////////////////////////
FastLineDetectorImpl::FastLineDetectorImpl(int _length_threshold, float _distance_threshold,
double _canny_th1, double _canny_th2, int _canny_aperture_size, bool _do_merge)
:threshold_length(_length_threshold), threshold_dist(_distance_threshold),
canny_th1(_canny_th1), canny_th2(_canny_th2), canny_aperture_size(_canny_aperture_size), do_merge(_do_merge)
{
CV_Assert(_length_threshold > 0 && _distance_threshold > 0 &&
_canny_th1 > 0 && _canny_th2 > 0 && _canny_aperture_size > 0);
}
void FastLineDetectorImpl::detect(InputArray _image, OutputArray _lines)
{
CV_INSTRUMENT_REGION();
Mat image = _image.getMat();
CV_Assert(!image.empty() && image.type() == CV_8UC1);
std::vector<Vec4f> lines;
std::vector<SEGMENT> segments;
lineDetection(image, segments);
for(size_t i = 0; i < segments.size(); ++i)
{
const SEGMENT seg = segments[i];
Vec4f line(seg.x1, seg.y1, seg.x2, seg.y2);
lines.push_back(line);
}
Mat(lines).copyTo(_lines);
}
void FastLineDetectorImpl::drawSegments(InputOutputArray _image, InputArray lines, bool draw_arrow)
{
CV_INSTRUMENT_REGION();
CV_Assert(!_image.empty() && (_image.channels() == 1 || _image.channels() == 3));
Mat gray;
if (_image.channels() == 1)
{
gray = _image.getMatRef();
}
else if (_image.channels() == 3)
{
cvtColor(_image, gray, COLOR_BGR2GRAY);
}
// Create a 3 channel image in order to draw colored lines
std::vector<Mat> planes;
planes.push_back(gray);
planes.push_back(gray);
planes.push_back(gray);
merge(planes, _image);
double gap = 10.0;
double arrow_angle = 30.0;
Mat _lines;
_lines = lines.getMat();
int N = _lines.checkVector(4);
// Draw segments
for(int i = 0; i < N; ++i)
{
const Vec4f& v = _lines.at<Vec4f>(i);
Point2f b(v[0], v[1]);
Point2f e(v[2], v[3]);
line(_image.getMatRef(), b, e, Scalar(0, 0, 255), 1);
if(draw_arrow)
{
SEGMENT seg;
seg.x1 = b.x;
seg.y1 = b.y;
seg.x2 = e.x;
seg.y2 = e.y;
getAngle(seg);
double ang = (double)seg.angle;
Point2i p1;
p1.x = (int)round(seg.x2 - gap*cos(arrow_angle * CV_PI / 180.0 + ang));
p1.y = (int)round(seg.y2 - gap*sin(arrow_angle * CV_PI / 180.0 + ang));
pointInboardTest(_image.getMatRef(), p1);
line(_image.getMatRef(), Point((int)round(seg.x2), (int)round(seg.y2)), p1, Scalar(0,0,255), 1);
}
}
}
void FastLineDetectorImpl::mergeLines(const SEGMENT& seg1, const SEGMENT& seg2, SEGMENT& seg_merged)
{
double xg = 0.0, yg = 0.0;
double delta1x = 0.0, delta1y = 0.0, delta2x = 0.0, delta2y = 0.0;
float ax = 0, bx = 0, cx = 0, dx = 0;
float ay = 0, by = 0, cy = 0, dy = 0;
double li = 0.0, lj = 0.0;
double thi = 0.0, thj = 0.0, thr = 0.0;
double axg = 0.0, bxg = 0.0, cxg = 0.0, dxg = 0.0, delta1xg = 0.0, delta2xg = 0.0;
ax = seg1.x1;
ay = seg1.y1;
bx = seg1.x2;
by = seg1.y2;
cx = seg2.x1;
cy = seg2.y1;
dx = seg2.x2;
dy = seg2.y2;
float dlix = (bx - ax);
float dliy = (by - ay);
float dljx = (dx - cx);
float dljy = (dy - cy);
li = sqrt((double) (dlix * dlix) + (double) (dliy * dliy));
lj = sqrt((double) (dljx * dljx) + (double) (dljy * dljy));
xg = (li * (double) (ax + bx) + lj * (double) (cx + dx))
/ (double) (2.0 * (li + lj));
yg = (li * (double) (ay + by) + lj * (double) (cy + dy))
/ (double) (2.0 * (li + lj));
if(dlix == 0.0f) thi = CV_PI / 2.0;
else thi = atan(dliy / dlix);
if(dljx == 0.0f) thj = CV_PI / 2.0;
else thj = atan(dljy / dljx);
if (fabs(thi - thj) <= CV_PI / 2.0)
{
thr = (li * thi + lj * thj) / (li + lj);
}
else
{
double tmp = thj - CV_PI * (thj / fabs(thj));
thr = li * thi + lj * tmp;
thr /= (li + lj);
}
axg = ((double) ay - yg) * sin(thr) + ((double) ax - xg) * cos(thr);
bxg = ((double) by - yg) * sin(thr) + ((double) bx - xg) * cos(thr);
cxg = ((double) cy - yg) * sin(thr) + ((double) cx - xg) * cos(thr);
dxg = ((double) dy - yg) * sin(thr) + ((double) dx - xg) * cos(thr);
delta1xg = min(axg,min(bxg,min(cxg,dxg)));
delta2xg = max(axg,max(bxg,max(cxg,dxg)));
delta1x = delta1xg * cos(thr) + xg;
delta1y = delta1xg * sin(thr) + yg;
delta2x = delta2xg * cos(thr) + xg;
delta2y = delta2xg * sin(thr) + yg;
seg_merged.x1 = (float)delta1x;
seg_merged.y1 = (float)delta1y;
seg_merged.x2 = (float)delta2x;
seg_merged.y2 = (float)delta2y;
}
double FastLineDetectorImpl::distPointLine(const Mat& p, Mat& l)
{
double x = l.at<double>(0,0);
double y = l.at<double>(1,0);
double w = sqrt(x*x+y*y);
l.at<double>(0,0) = x / w;
l.at<double>(1,0) = y / w;
l.at<double>(2,0) = l.at<double>(2,0) / w;
return l.dot(p);
}
bool FastLineDetectorImpl::mergeSegments(const SEGMENT& seg1, const SEGMENT& seg2, SEGMENT& seg_merged)
{
double o[] = { 0.0, 0.0, 1.0 };
double a[] = { 0.0, 0.0, 1.0 };
double b[] = { 0.0, 0.0, 1.0 };
double c[3];
o[0] = ( seg2.x1 + seg2.x2 ) / 2.0;
o[1] = ( seg2.y1 + seg2.y2 ) / 2.0;
a[0] = seg1.x1;
a[1] = seg1.y1;
b[0] = seg1.x2;
b[1] = seg1.y2;
Mat ori = Mat(3, 1, CV_64FC1, o).clone();
Mat p1 = Mat(3, 1, CV_64FC1, a).clone();
Mat p2 = Mat(3, 1, CV_64FC1, b).clone();
Mat l1 = Mat(3, 1, CV_64FC1, c).clone();
l1 = p1.cross(p2);
Point2f seg1mid, seg2mid;
seg1mid.x = (seg1.x1 + seg1.x2) /2.0f;
seg1mid.y = (seg1.y1 + seg1.y2) /2.0f;
seg2mid.x = (seg2.x1 + seg2.x2) /2.0f;
seg2mid.y = (seg2.y1 + seg2.y2) /2.0f;
float seg1len = sqrt((seg1.x1 - seg1.x2)*(seg1.x1 - seg1.x2)+(seg1.y1 - seg1.y2)*(seg1.y1 - seg1.y2));
float seg2len = sqrt((seg2.x1 - seg2.x2)*(seg2.x1 - seg2.x2)+(seg2.y1 - seg2.y2)*(seg2.y1 - seg2.y2));
float middist = sqrt((seg1mid.x - seg2mid.x)*(seg1mid.x - seg2mid.x) + (seg1mid.y - seg2mid.y)*(seg1mid.y - seg2mid.y));
float angdiff = fabs(seg1.angle - seg2.angle);
float dist = (float)distPointLine(ori, l1);
if ( fabs( dist ) <= threshold_dist * 2.0f && middist <= seg1len / 2.0f + seg2len / 2.0f + 20.0f
&& angdiff <= CV_PI / 180.0f * 5.0f)
{
mergeLines(seg1, seg2, seg_merged);
return true;
}
else
{
return false;
}
}
template<class T>
void FastLineDetectorImpl::incidentPoint(const Mat& l, T& pt)
{
double a[] = { (double)pt.x, (double)pt.y, 1.0 };
double b[] = { l.at<double>(0,0), l.at<double>(1,0), 0.0 };
double c[3];
Mat xk = Mat(3, 1, CV_64FC1, a).clone();
Mat lh = Mat(3, 1, CV_64FC1, b).clone();
Mat lk = Mat(3, 1, CV_64FC1, c).clone();
lk = xk.cross(lh);
xk = lk.cross(l);
xk.convertTo(xk, -1, 1.0 / xk.at<double>(2,0));
Point2f pt_tmp;
pt_tmp.x = (float)xk.at<double>(0,0) < 0.0f ? 0.0f : (float)xk.at<double>(0,0)
>= (imagewidth - 1.0f) ? (imagewidth - 1.0f) : (float)xk.at<double>(0,0);
pt_tmp.y = (float)xk.at<double>(1,0) < 0.0f ? 0.0f : (float)xk.at<double>(1,0)
>= (imageheight - 1.0f) ? (imageheight - 1.0f) : (float)xk.at<double>(1,0);
pt = T(pt_tmp);
}
void FastLineDetectorImpl::extractSegments(const std::vector<Point2i>& points, std::vector<SEGMENT>& segments )
{
bool is_line;
int i, j;
SEGMENT seg;
Point2i ps, pe, pt;
std::vector<Point2i> l_points;
int total = (int)points.size();
for ( i = 0; i + threshold_length < total; i++ )
{
ps = points[i];
pe = points[i + threshold_length];
double a[] = { (double)ps.x, (double)ps.y, 1 };
double b[] = { (double)pe.x, (double)pe.y, 1 };
double c[3], d[3];
Mat p1 = Mat(3, 1, CV_64FC1, a).clone();
Mat p2 = Mat(3, 1, CV_64FC1, b).clone();
Mat p = Mat(3, 1, CV_64FC1, c).clone();
Mat l = Mat(3, 1, CV_64FC1, d).clone();
l = p1.cross(p2);
is_line = true;
l_points.clear();
l_points.push_back(ps);
for ( j = 1; j < threshold_length; j++ )
{
pt.x = points[i+j].x;
pt.y = points[i+j].y;
p.at<double>(0,0) = (double)pt.x;
p.at<double>(1,0) = (double)pt.y;
p.at<double>(2,0) = 1.0;
double dist = distPointLine(p, l);
if ( fabs( dist ) > threshold_dist )
{
is_line = false;
break;
}
l_points.push_back(pt);
}
// Line check fail, test next point
if ( is_line == false )
continue;
l_points.push_back(pe);
Vec4f line;
fitLine( Mat(l_points), line, DIST_L2, 0, 0.01, 0.01);
a[0] = line[2];
a[1] = line[3];
b[0] = line[2] + line[0];
b[1] = line[3] + line[1];
p1 = Mat(3, 1, CV_64FC1, a).clone();
p2 = Mat(3, 1, CV_64FC1, b).clone();
l = p1.cross(p2);
incidentPoint(l, ps);
// Extending line
for ( j = threshold_length + 1; i + j < total; j++ )
{
pt.x = points[i+j].x;
pt.y = points[i+j].y;
p.at<double>(0,0) = (double)pt.x;
p.at<double>(1,0) = (double)pt.y;
p.at<double>(2,0) = 1.0;
double dist = distPointLine(p, l);
if ( fabs( dist ) > threshold_dist )
{
fitLine( Mat(l_points), line, DIST_L2, 0, 0.01, 0.01);
a[0] = line[2];
a[1] = line[3];
b[0] = line[2] + line[0];
b[1] = line[3] + line[1];
p1 = Mat(3, 1, CV_64FC1, a).clone();
p2 = Mat(3, 1, CV_64FC1, b).clone();
l = p1.cross(p2);
dist = distPointLine(p, l);
if ( fabs( dist ) > threshold_dist ) {
j--;
break;
}
}
pe = pt;
l_points.push_back(pt);
}
fitLine( Mat(l_points), line, DIST_L2, 0, 0.01, 0.01);
a[0] = line[2];
a[1] = line[3];
b[0] = line[2] + line[0];
b[1] = line[3] + line[1];
p1 = Mat(3, 1, CV_64FC1, a).clone();
p2 = Mat(3, 1, CV_64FC1, b).clone();
l = p1.cross(p2);
Point2f e1, e2;
e1.x = (float)ps.x;
e1.y = (float)ps.y;
e2.x = (float)pe.x;
e2.y = (float)pe.y;
incidentPoint(l, e1);
incidentPoint(l, e2);
seg.x1 = e1.x;
seg.y1 = e1.y;
seg.x2 = e2.x;
seg.y2 = e2.y;
segments.push_back(seg);
i = i + j;
}
}
void FastLineDetectorImpl::pointInboardTest(const Mat& src, Point2i& pt)
{
pt.x = pt.x <= 5 ? 5 : pt.x >= src.cols - 5 ? src.cols - 5 : pt.x;
pt.y = pt.y <= 5 ? 5 : pt.y >= src.rows - 5 ? src.rows - 5 : pt.y;
}
bool FastLineDetectorImpl::getPointChain(const Mat& img, Point pt,
Point& chained_pt, float& direction, int step)
{
int ri, ci;
int indices[8][2] = { {1,1}, {1,0}, {1,-1}, {0,-1},
{-1,-1},{-1,0}, {-1,1}, {0,1} };
float min_dir_diff = 7.0f;
Point consistent_pt;
int consistent_direction = 0;
for ( int i = 0; i < 8; i++ )
{
ci = pt.x + indices[i][1];
ri = pt.y + indices[i][0];
if ( ri < 0 || ri == img.rows || ci < 0 || ci == img.cols )
continue;
if ( img.at<unsigned char>(ri, ci) == 0 )
continue;
if(step == 0)
{
chained_pt.x = ci;
chained_pt.y = ri;
// direction = (float)i;
direction = i > 4 ? (float)(i - 8) : (float)i;
return true;
}
else
{
float curr_dir = i > 4 ? (float)(i - 8) : (float)i;
float dir_diff = abs(curr_dir - direction);
dir_diff = dir_diff > 4.0f ? 8.0f - dir_diff : dir_diff;
if(dir_diff <= min_dir_diff)
{
min_dir_diff = dir_diff;
consistent_pt.x = ci;
consistent_pt.y = ri;
consistent_direction = i > 4 ? i - 8 : i;
}
}
}
if(min_dir_diff < 2.0f)
{
chained_pt.x = consistent_pt.x;
chained_pt.y = consistent_pt.y;
direction = (direction * (float)step + (float)consistent_direction)
/ (float)(step + 1);
return true;
}
return false;
}
void FastLineDetectorImpl::lineDetection(const Mat& src, std::vector<SEGMENT>& segments_all)
{
int r, c;
imageheight=src.rows; imagewidth=src.cols;
std::vector<Point2i> points;
std::vector<SEGMENT> segments, segments_tmp;
Mat canny;
Canny(src, canny, canny_th1, canny_th2, canny_aperture_size);
canny.colRange(0,6).rowRange(0,6) = 0;
canny.colRange(src.cols-5,src.cols).rowRange(src.rows-5,src.rows) = 0;
SEGMENT seg, seg1, seg2;
for ( r = 0; r < imageheight; r++ )
{
for ( c = 0; c < imagewidth; c++ )
{
// Find seeds - skip for non-seeds
if ( canny.at<unsigned char>(r,c) == 0 )
continue;
// Found seeds
Point2i pt = Point2i(c,r);
points.push_back(pt);
canny.at<unsigned char>(pt.y, pt.x) = 0;
float direction = 0.0f;
int step = 0;
while(getPointChain(canny, pt, pt, direction, step))
{
points.push_back(pt);
step++;
canny.at<unsigned char>(pt.y, pt.x) = 0;
}
if ( points.size() < (unsigned int)threshold_length + 1 )
{
points.clear();
continue;
}
extractSegments(points, segments);
if ( segments.size() == 0 )
{
points.clear();
continue;
}
for ( int i = 0; i < (int)segments.size(); i++ )
{
seg = segments[i];
float length = sqrt((seg.x1 - seg.x2)*(seg.x1 - seg.x2) +
(seg.y1 - seg.y2)*(seg.y1 - seg.y2));
if(length < threshold_length)
continue;
if( (seg.x1 <= 5.0f && seg.x2 <= 5.0f) ||
(seg.y1 <= 5.0f && seg.y2 <= 5.0f) ||
(seg.x1 >= imagewidth - 5.0f && seg.x2 >= imagewidth - 5.0f) ||
(seg.y1 >= imageheight - 5.0f && seg.y2 >= imageheight - 5.0f) )
continue;
additionalOperationsOnSegment(src, seg);
if(!do_merge)
segments_all.push_back(seg);
segments_tmp.push_back(seg);
}
points.clear();
segments.clear();
}
}
if(!do_merge)
return;
bool is_merged = false;
int ith = (int)segments_tmp.size() - 1;
int jth = ith - 1;
while(ith > 1 || jth > 0)
{
seg1 = segments_tmp[ith];
seg2 = segments_tmp[jth];
SEGMENT seg_merged;
is_merged = mergeSegments(seg1, seg2, seg_merged);
if(is_merged == true)
{
seg2 = seg_merged;
additionalOperationsOnSegment(src, seg2);
std::vector<SEGMENT>::iterator it = segments_tmp.begin() + ith;
*it = seg2;
segments_tmp.erase(segments_tmp.begin()+jth);
ith--;
jth = ith - 1;
}
else
{
jth--;
}
if(jth < 0) {
ith--;
jth = ith - 1;
}
}
segments_all = segments_tmp;
}
inline void FastLineDetectorImpl::getAngle(SEGMENT& seg)
{
seg.angle = (float)(fastAtan2(seg.y2 - seg.y1, seg.x2 - seg.x1) / 180.0f * CV_PI);
}
void FastLineDetectorImpl::additionalOperationsOnSegment(const Mat& src, SEGMENT& seg)
{
if(seg.x1 == 0.0f && seg.x2 == 0.0f && seg.y1 == 0.0f && seg.y2 == 0.0f)
return;
getAngle(seg);
double ang = (double)seg.angle;
Point2f start = Point2f(seg.x1, seg.y1);
Point2f end = Point2f(seg.x2, seg.y2);
double dx = 0.0, dy = 0.0;
dx = (double) end.x - (double) start.x;
dy = (double) end.y - (double) start.y;
int num_points = 10;
Point2f *points = new Point2f[num_points];
points[0] = start;
points[num_points - 1] = end;
for (int i = 0; i < num_points; i++)
{
if (i == 0 || i == num_points - 1)
continue;
points[i].x = points[0].x + ((float)dx / float(num_points - 1) * (float) i);
points[i].y = points[0].y + ((float)dy / float(num_points - 1) * (float) i);
}
Point2i *points_right = new Point2i[num_points];
Point2i *points_left = new Point2i[num_points];
double gap = 1.0;
for(int i = 0; i < num_points; i++)
{
points_right[i].x = cvRound(points[i].x + gap*cos(90.0 * CV_PI / 180.0 + ang));
points_right[i].y = cvRound(points[i].y + gap*sin(90.0 * CV_PI / 180.0 + ang));
points_left[i].x = cvRound(points[i].x - gap*cos(90.0 * CV_PI / 180.0 + ang));
points_left[i].y = cvRound(points[i].y - gap*sin(90.0 * CV_PI / 180.0 + ang));
pointInboardTest(src, points_right[i]);
pointInboardTest(src, points_left[i]);
}
int iR = 0, iL = 0;
for(int i = 0; i < num_points; i++)
{
iR += src.at<unsigned char>(points_right[i].y, points_right[i].x);
iL += src.at<unsigned char>(points_left[i].y, points_left[i].x);
}
if(iR > iL)
{
std::swap(seg.x1, seg.x2);
std::swap(seg.y1, seg.y2);
getAngle(seg);
}
delete[] points;
delete[] points_right;
delete[] points_left;
return;
}
void FastLineDetectorImpl::drawSegment(Mat& mat, const SEGMENT& seg, Scalar bgr, int thickness, bool directed)
{
double gap = 10.0;
double ang = (double)seg.angle;
double arrow_angle = 30.0;
Point2i p1;
p1.x = (int)round(seg.x2 - gap*cos(arrow_angle * CV_PI / 180.0 + ang));
p1.y = (int)round(seg.y2 - gap*sin(arrow_angle * CV_PI / 180.0 + ang));
pointInboardTest(mat, p1);
line(mat, Point((int)round(seg.x1), (int)round(seg.y1)),
Point((int)round(seg.x2), (int)round(seg.y2)), bgr, thickness, 1);
if(directed)
line(mat, Point((int)round(seg.x2), (int)round(seg.y2)), p1, bgr, thickness, 1);
}
} // namespace cv
} // namespace ximgproc
#include "test_precomp.hpp"
#include <vector>
using namespace cv;
using namespace cv::ximgproc;
using namespace std;
const Size img_size(640, 480);
const int FLD_TEST_SEED = 0x134679;
const int EPOCHS = 20;
class FLDBase : public testing::Test
{
public:
FLDBase() { }
protected:
Mat test_image;
vector<Vec4f> lines;
RNG rng;
int passedtests;
void GenerateWhiteNoise(Mat& image);
void GenerateConstColor(Mat& image);
void GenerateLines(Mat& image, const unsigned int numLines);
void GenerateBrokenLines(Mat& image, const unsigned int numLines);
void GenerateRotatedRect(Mat& image);
virtual void SetUp();
};
class ximgproc_FLD: public FLDBase
{
public:
ximgproc_FLD() { }
protected:
};
void FLDBase::GenerateWhiteNoise(Mat& image)
{
image = Mat(img_size, CV_8UC1);
rng.fill(image, RNG::UNIFORM, 0, 256);
}
void FLDBase::GenerateConstColor(Mat& image)
{
image = Mat(img_size, CV_8UC1, Scalar::all(rng.uniform(0, 256)));
}
void FLDBase::GenerateLines(Mat& image, const unsigned int numLines)
{
image = Mat(img_size, CV_8UC1, Scalar::all(rng.uniform(0, 128)));
for(unsigned int i = 0; i < numLines; ++i)
{
int y = rng.uniform(10, img_size.width - 10);
Point p1(y, 10);
Point p2(y, img_size.height - 10);
line(image, p1, p2, Scalar(255), 2);
}
}
void FLDBase::GenerateBrokenLines(Mat& image, const unsigned int numLines)
{
image = Mat(img_size, CV_8UC1, Scalar::all(rng.uniform(0, 128)));
for(unsigned int i = 0; i < numLines; ++i)
{
int y = rng.uniform(10, img_size.width - 10);
Point p1(y, 10);
Point p2(y, img_size.height/2);
line(image, p1, p2, Scalar(255), 2);
p1 = Point2i(y, img_size.height/2 + 3);
p2 = Point2i(y, img_size.height - 10);
line(image, p1, p2, Scalar(255), 2);
}
}
void FLDBase::GenerateRotatedRect(Mat& image)
{
image = Mat::zeros(img_size, CV_8UC1);
Point center(rng.uniform(img_size.width/4, img_size.width*3/4),
rng.uniform(img_size.height/4, img_size.height*3/4));
Size rect_size(rng.uniform(img_size.width/8, img_size.width/6),
rng.uniform(img_size.height/8, img_size.height/6));
float angle = rng.uniform(0.f, 360.f);
Point2f vertices[4];
RotatedRect rRect = RotatedRect(center, rect_size, angle);
rRect.points(vertices);
for (int i = 0; i < 4; i++)
{
line(image, vertices[i], vertices[(i + 1) % 4], Scalar(255), 3);
}
}
void FLDBase::SetUp()
{
lines.clear();
test_image = Mat();
rng = RNG(FLD_TEST_SEED);
passedtests = 0;
}
TEST_F(ximgproc_FLD, whiteNoise)
{
for (int i = 0; i < EPOCHS; ++i)
{
GenerateWhiteNoise(test_image);
Ptr<FastLineDetector> detector = createFastLineDetector(20);
detector->detect(test_image, lines);
if(40u >= lines.size()) ++passedtests;
}
ASSERT_EQ(EPOCHS, passedtests);
}
TEST_F(ximgproc_FLD, constColor)
{
for (int i = 0; i < EPOCHS; ++i)
{
GenerateConstColor(test_image);
Ptr<FastLineDetector> detector = createFastLineDetector();
detector->detect(test_image, lines);
if(0u == lines.size()) ++passedtests;
}
ASSERT_EQ(EPOCHS, passedtests);
}
TEST_F(ximgproc_FLD, lines)
{
for (int i = 0; i < EPOCHS; ++i)
{
const unsigned int numOfLines = 1;
GenerateLines(test_image, numOfLines);
Ptr<FastLineDetector> detector = createFastLineDetector();
detector->detect(test_image, lines);
if(numOfLines * 2 == lines.size()) ++passedtests; // * 2 because of Gibbs effect
}
ASSERT_EQ(EPOCHS, passedtests);
}
TEST_F(ximgproc_FLD, mergeLines)
{
for (int i = 0; i < EPOCHS; ++i)
{
const unsigned int numOfLines = 1;
GenerateBrokenLines(test_image, numOfLines);
Ptr<FastLineDetector> detector = createFastLineDetector(10, 1.414213562f, true);
detector->detect(test_image, lines);
if(numOfLines * 2 == lines.size()) ++passedtests; // * 2 because of Gibbs effect
}
ASSERT_EQ(EPOCHS, passedtests);
}
TEST_F(ximgproc_FLD, rotatedRect)
{
for (int i = 0; i < EPOCHS; ++i)
{
GenerateRotatedRect(test_image);
Ptr<FastLineDetector> detector = createFastLineDetector();
detector->detect(test_image, lines);
if(2u <= lines.size()) ++passedtests;
}
ASSERT_EQ(EPOCHS, passedtests);
}
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