MRPT  1.9.9
CFeatureLines.cpp
Go to the documentation of this file.
1 /* +---------------------------------------------------------------------------+
2  | Mobile Robot Programming Toolkit (MRPT) |
3  | http://www.mrpt.org/ |
4  | |
5  | Copyright (c) 2005-2017, Individual contributors, see AUTHORS file |
6  | See: http://www.mrpt.org/Authors - All rights reserved. |
7  | Released under BSD License. See details in http://www.mrpt.org/License |
8  +---------------------------------------------------------------------------+ */
9 
10 #include "vision-precomp.h" // Precompiled headers
11 
13 #include <mrpt/utils/CTicTac.h>
14 
15 // Universal include for all versions of OpenCV
16 #include <mrpt/otherlibs/do_opencv_includes.h>
17 
18 #if MRPT_HAS_OPENCV
19 
20 using namespace mrpt::vision;
21 using namespace mrpt::utils;
22 using namespace std;
23 
24 
25 /************************************************************************************************
26 * extractLines *
27 ************************************************************************************************/
28 void CFeatureLines::extractLines(const cv::Mat & image,
29  std::vector<cv::Vec4i> & segments,
30  size_t threshold,
31  const bool display )
32 {
33  cout << "CFeatureLines::extractLines... threshold " << threshold << endl;
34  CTicTac clock; clock.Tic(); //Clock to measure the runtime
35 
36  // Canny edge detector
37  cv::Mat canny_img;
38  int lowThreshold = 150;
39  //int const max_lowThreshold = 100;
40  int ratio = 3;
41  int kernel_size = 3;
42  cv::Canny(image, canny_img, lowThreshold, lowThreshold*ratio, kernel_size); // 250, 600 // CAUTION: Both thresholds depend on the input image, they might be a bit hard to set because they depend on the strength of the gradients
43  // cv::namedWindow("Canny detector", cv::WINDOW_AUTOSIZE);
44  // cv::imshow("Canny detector", canny_img);
45  // cv::waitKey(0);
46  // cv::destroyWindow("Canny detector");
47 
48  // Get lines through the Hough transform
49  std::vector<cv::Vec2f> lines;
50  cv::HoughLines(canny_img, lines, 1, CV_PI / 180.0, threshold); // CAUTION: The last parameter depends on the input image, it's the smallest number of pixels to consider a line in the accumulator
51  // double minLineLength=50, maxLineGap=5;
52  // cv::HoughLinesP(canny_img, lines, 1, CV_PI / 180.0, threshold, minLineLength, maxLineGap); // CAUTION: The last parameter depends on the input image, it's the smallest number of pixels to consider a line in the accumulator
53  //std::cout << lines.size() << " lines detected" << std::endl;
54 
55  // Possible dilatation of the canny detector
56  // Useful when the lines are thin and not perfectly straight
57  cv::Mat filteredCanny;
58  // Choose whether filtering the Canny or not, for thin and non-perfect edges
59  /*filteredCanny = canny_img.clone();*/
60  cv::dilate(canny_img, filteredCanny, cv::Mat());
61 // cv::namedWindow("Filtered Canny detector");
62 // cv::imshow("Filtered Canny detector", filteredCanny);
63 // cv::waitKey(0);
64 // cv::destroyWindow("Filtered Canny detector");
65 
66  // Extracting segments (pairs of points) from the filtered Canny detector
67  // And using the line parameters from lines
68  extractLines_CannyHough(filteredCanny, lines, segments, threshold);
69  cout << " CFeatureLines::extractLines " << segments.size() << " took " << 1000*clock.Tac() << " ms \n";
70 
71  // Force the segments to have a predefined order (e1y <= e2y)
72  for(auto line = begin(segments); line != end(segments); ++line)
73  if((*line)[1] == (*line)[3]){
74  if((*line)[0] > (*line)[1])
75  *line = cv::Vec4i((*line)[2], (*line)[3], (*line)[0], (*line)[1]);
76  }
77  else if((*line)[1] < (*line)[3])
78  *line = cv::Vec4i((*line)[2], (*line)[3], (*line)[0], (*line)[1]);
79 
80  // Display 2D segments
81  if(display)
82  {
83  cv::Mat image_lines;
84  image.convertTo(image_lines, CV_8UC1, 1.0 / 2);
85  for(auto line = begin(segments); line != end(segments); ++line)
86  {
87  //cout << "line: " << (*line)[0] << " " << (*line)[1] << " " << (*line)[2] << " " << (*line)[3] << " \n";
88  //image.convertTo(image_lines, CV_8UC1, 1.0 / 2);
89  cv::line(image_lines, cv::Point((*line)[0], (*line)[1]), cv::Point((*line)[2], (*line)[3]), cv::Scalar(255, 0, 255), 1);
90  cv::circle(image_lines, cv::Point((*line)[0], (*line)[1]), 3, cv::Scalar(255, 0, 255), 3);
91  cv::putText(image_lines, string(to_string(distance(begin(segments),line))), cv::Point(((*line)[0]+(*line)[2])/2,((*line)[1]+(*line)[3])/2), 0, 1.2, cv::Scalar(200,0,0), 3 );
92  //cv::imshow("lines", image_lines); cv::moveWindow("lines", 20,100+700);
93  //cv::waitKey(0);
94  }
95  cv::imwrite("/home/efernand/lines.png", image_lines);
96  cv::imshow("lines", image_lines); cv::moveWindow("lines", 20,100+700);
97  cv::waitKey(0);
98  }
99 }
100 
101 void CFeatureLines::extractLines_CannyHough( const cv::Mat & canny_image,
102  const std::vector<cv::Vec2f> lines,
103  std::vector<cv::Vec4i> & segments,
104  size_t threshold )
105 {
106  // Some variables to change the coordinate system from polar to cartesian
107  double rho, theta;
108  double cosTheta, sinTheta;
109  double m, c, cMax;
110 
111  // Variables to define the two extreme points on the line, clipped on the window
112  // The constraint is x <= xF
113  int x, y, xF, yF;
114 
115  segments.clear();
116 
117  // For each line
118  for (size_t n(0); n < lines.size(); ++n) {
119 
120  // OpenCV implements the Hough accumulator with theta from 0 to PI, thus rho could be negative
121  // We want to always have rho >= 0
122  if (lines[n][0] < 0) {
123  rho = -lines[n][0];
124  theta = lines[n][1] - CV_PI;
125  }
126  else {
127  rho = lines[n][0];
128  theta = lines[n][1];
129  }
130 
131  if (rho == 0 && theta == 0) { // That case appeared at least once, so a test is needed
132  continue;
133  }
134 
135  // Since the step for theta in cv::HoughLines should not be too small,
136  // theta should exactly be 0 (and the line vertical) when this condition is true
137  if (fabs(theta) < 0.00001)
138  {
139  x = xF = static_cast<int>(rho + 0.5);
140  y = 0;
141  yF = canny_image.rows - 1;
142  }
143  else {
144  // Get the (m, c) slope and y-intercept from (rho, theta), so that y = mx + c <=> (x, y) is on the line
145  cosTheta = cos(theta);
146  sinTheta = sin(theta);
147  m = -cosTheta / sinTheta; // We are certain that sinTheta != 0
148  c = rho * (sinTheta - m * cosTheta);
149 
150  // Get the two extreme points (x, y) and (xF, xF) for the line inside the window, using (m, c)
151  if (c >= 0) {
152  if (c < canny_image.rows) {
153  // (x, y) is at the left of the window
154  x = 0;
155  y = static_cast<int>(c);
156  }
157  else {
158  // (x, y) is at the bottom of the window
159  y = canny_image.rows - 1;
160  x = static_cast<int>((y - c) / m);
161  }
162  }
163  else {
164  // (x, y) is at the top of the window
165  x = static_cast<int>(-c / m);
166  y = 0;
167  }
168  // Define the intersection with the right side of the window
169  cMax = m * (canny_image.cols - 1) + c;
170  if (cMax >= 0) {
171  if (cMax < canny_image.rows) {
172  // (xF, yF) is at the right of the window
173  xF = canny_image.cols - 1;
174  yF = static_cast<int>(cMax);
175  }
176  else {
177  // (xF, yF) is at the bottom of the window
178  yF = canny_image.rows - 1;
179  xF = static_cast<int>((yF - c) / m);
180  }
181  }
182  else {
183  // (xF, yF) is at the top of the window
184  xF = static_cast<int>(-c / m);
185  yF = 0;
186  }
187 
188  }
189 
190  // Going through the line using the Bresenham algorithm
191  // dx1, dx2, dy1 and dy2 are increments that allow to be successful for each of the 8 octants (possible directions while iterating)
192  bool onSegment = false;
193  int memory;
194  int memoryX = 0, memoryY = 0;
195  int xPrev = 0, yPrev = 0;
196  size_t nbPixels = 0;
197 
198  int w = xF - x;
199  int h = yF - y;
200  int dx1, dy1, dx2, dy2 = 0;
201 
202  int longest, shortest;
203  int numerator;
204 
205  if (w < 0)
206  {
207  longest = -w;
208  dx1 = -1;
209  dx2 = -1;
210  }
211  else
212  {
213  longest = w;
214  dx1 = 1;
215  dx2 = 1;
216  }
217 
218  if (h < 0) {
219  shortest = -h;
220  dy1 = -1;
221  }
222  else {
223  shortest = h;
224  dy1 = 1;
225  }
226 
227  // We want to know whether the direction is more horizontal or vertical, and set the increments accordingly
228  if (longest <= shortest)
229  {
230  memory = longest;
231  longest = shortest;
232  shortest = memory;
233  dx2 = 0;
234  if (h < 0) {
235  dy2 = -1;
236  }
237  else {
238  dy2 = 1;
239  }
240  }
241 
242  numerator = longest / 2;
243 
244  //cout << n << " numerator " << numerator << endl;
245  for (int i(0); i <= longest; ++i)
246  {
247  // For each pixel, we don't want to use a classic "plot", but to look into canny_image for a black or white pixel
248  if (onSegment) {
249  if (canny_image.at<char>(y, x) == 0 || i == longest)
250  {
251  // We are leaving a segment
252  onSegment = false;
253  if (nbPixels >= threshold) {
254  segments.push_back(cv::Vec4i(memoryX, memoryY, xPrev, yPrev));
255  //cout << "new segment " << segments.back() << endl;
256  }
257  }
258  else
259  {
260  // We are still on a segment
261  ++nbPixels;
262  }
263  }
264  else if (canny_image.at<char>(y, x) != 0)
265  {
266  // We are entering a segment, and keep this first position in (memoryX, memoryY)
267  onSegment = true;
268  nbPixels = 0;
269  memoryX = x;
270  memoryY = y;
271  }
272 
273  // xPrev and yPrev are used when leaving a segment, to keep in memory the last pixel on it
274  xPrev = x;
275  yPrev = y;
276 
277  // Next pixel using the condition of the Bresenham algorithm
278  numerator += shortest;
279  if (numerator >= longest)
280  {
281  numerator -= longest;
282  x += dx1;
283  y += dy1;
284  }
285  else {
286  x += dx2;
287  y += dy2;
288  }
289  }
290 
291  }
292 
293 }
294 
295 #endif //MRPT_END
double Tac() noexcept
Stops the stopwatch.
Definition: CTicTac.cpp:90
double Scalar
Definition: KmUtils.h:44
#define CV_PI
GLenum GLsizei n
Definition: glext.h:5074
A high-performance stopwatch, with typical resolution of nanoseconds.
EIGEN_STRONG_INLINE iterator begin()
Definition: eigen_plugins.h:29
GLenum GLsizei GLenum GLenum const GLvoid * image
Definition: glext.h:3551
STL namespace.
void extractLines(const cv::Mat &image, std::vector< cv::Vec4i > &segments, size_t threshold, const bool display=false)
GLubyte GLubyte GLubyte GLubyte w
Definition: glext.h:4178
const GLubyte * c
Definition: glext.h:6313
GLuint GLuint end
Definition: glext.h:3528
void extractLines_CannyHough(const cv::Mat &canny_image, const std::vector< cv::Vec2f > lines, std::vector< cv::Vec4i > &segments, size_t threshold)
Classes for computer vision, detectors, features, etc.
Definition: CCamModel.h:18
GLenum GLint GLint y
Definition: glext.h:3538
std::string std::string to_string(T v)
Just like std::to_string(), but with an overloaded version for std::string arguments.
Definition: format.h:30
GLenum GLint x
Definition: glext.h:3538
void Tic() noexcept
Starts the stopwatch.
Definition: CTicTac.cpp:79
double distance(const TPoint2D &p1, const TPoint2D &p2)
Gets the distance between two points in a 2D space.
Definition: geometry.cpp:1891



Page generated by Doxygen 1.8.14 for MRPT 1.9.9 Git: 7d5e6d718 Fri Aug 24 01:51:28 2018 +0200 at lun nov 2 08:35:50 CET 2020