Lane and Object Detection
Using OpenCV and YOLOv7
Loading...
Searching...
No Matches
LaneDetector.cpp
1#include <algorithm>
2#include <cmath>
3#include <cstdint>
4#include <deque>
5#include <limits>
6#include <string>
7#include <unordered_map>
8#include <utility>
9#include <vector>
10
11#include <opencv2/core.hpp>
12#include <opencv2/core/mat.hpp>
13#include <opencv2/core/matx.hpp>
14#include <opencv2/core/types.hpp>
15#include <opencv2/imgproc.hpp>
16
17#include "helpers/Globals.hpp"
18#include "helpers/Information.hpp"
19
20#include "detectors/LaneDetector.hpp"
21
23{
33
34 void LaneDetector::RunLaneDetector(const cv::Mat& p_frame, const ObjectDetectionInformation& p_objectDetectionInformation, const bool& p_debugMode)
35 {
36 // Get region of interest (ROI) frame by applying a mask on to the frame
37 cv::Mat blankFrame = cv::Mat::zeros(Globals::G_VIDEO_INPUT_HEIGHT, Globals::G_VIDEO_INPUT_WIDTH, p_frame.type());
38 cv::fillConvexPoly(blankFrame, Globals::G_ROI_MASK_POINTS, Globals::G_COLOUR_WHITE);
39 cv::bitwise_and(blankFrame, p_frame, m_laneDetectionInformation.m_roiFrame);
40 cv::cvtColor(m_laneDetectionInformation.m_roiFrame, m_laneDetectionInformation.m_roiFrame, cv::COLOR_BGR2GRAY);
41
42 // Get edges using Canny Algorithm on the ROI Frame
44
45 // Get straight lines using the Probabilistic Hough Transform (PHT) on te output of Canny Algorithm
46 std::vector<cv::Vec4i> houghLines;
47 m_laneDetectionInformation.m_houghLinesFrame = cv::Mat::zeros(Globals::G_VIDEO_INPUT_HEIGHT, Globals::G_VIDEO_INPUT_WIDTH, p_frame.type());
49
50 AnalyseHoughLines(houghLines, p_objectDetectionInformation, p_debugMode);
51
52 if (p_debugMode)
53 {
57
61 }
62
64
66
68 }
69
74
75 template<typename T>
80
81 template<typename T>
83 {
84 m_rollingAverageArray.pop_back();
85 m_rollingAverageArray.push_front(p_nextInput);
86
87 std::unordered_map<T, uint32_t> elementCount;
88 for (const T& element : m_rollingAverageArray)
89 {
90 elementCount[element]++;
91 }
92
93 T mostFrequentElement = static_cast<T>(0);
94 uint32_t mostFrequentElementCount = 0;
95
96 for (const std::pair<T, uint32_t> element : elementCount) // NOLINT(readability-identifier-naming)
97 {
98 if (element.second > mostFrequentElementCount)
99 {
100 mostFrequentElement = element.first;
101 mostFrequentElementCount = element.second;
102 }
103 }
104
105 return mostFrequentElement;
106 }
108 void LaneDetector::AnalyseHoughLines(const std::vector<cv::Vec4i>& p_houghLines, const ObjectDetectionInformation& p_objectDetectionInformation, const bool& p_debugMode) // NOLINT(readability-function-cognitive-complexity)
109 {
110 m_leftLaneLines.clear();
111 m_middleLaneLines.clear();
112 m_rightLaneLines.clear();
117 for (const cv::Vec4i& houghLine : p_houghLines)
118 {
119 const cv::Point POINT_ONE = cv::Point(houghLine[Globals::G_VEC4_X1_INDEX], houghLine[Globals::G_VEC4_Y1_INDEX]);
120 const cv::Point POINT_TWO = cv::Point(houghLine[Globals::G_VEC4_X2_INDEX], houghLine[Globals::G_VEC4_Y2_INDEX]);
121
122 const double CHANGE_IN_X = houghLine[Globals::G_VEC4_X1_INDEX] - houghLine[Globals::G_VEC4_X2_INDEX];
123 const double CHANGE_IN_Y = houghLine[Globals::G_VEC4_Y1_INDEX] - houghLine[Globals::G_VEC4_Y2_INDEX];
124
125 // Filter out vertical lines
126 if (CHANGE_IN_X == 0)
127 {
128 if (p_debugMode)
129 {
130 cv::line(m_laneDetectionInformation.m_houghLinesFrame, POINT_ONE, POINT_TWO, Globals::G_COLOUR_WHITE, Globals::G_HOUGH_LINE_THICKNESS, cv::LINE_AA);
131 }
132 continue;
133 }
134
135 const double GRADIENT = CHANGE_IN_Y / CHANGE_IN_X;
136
137 // Filter out horizontal lines
139 {
140 if (p_debugMode)
141 {
142 cv::line(m_laneDetectionInformation.m_houghLinesFrame, POINT_ONE, POINT_TWO, Globals::G_COLOUR_GREY, Globals::G_HOUGH_LINE_THICKNESS, cv::LINE_AA);
143 }
144 continue;
145 }
146
147 // The bounding boxes are created by the object detector which cannot detect road markings. Thus, if the line is in
148 // a bounding box, it cannot be a road marking and is ignored.
149 if (IsLineWithinObjectBoundingBoxes(houghLine, p_objectDetectionInformation))
150 {
151 if (p_debugMode)
152 {
153 cv::line(m_laneDetectionInformation.m_houghLinesFrame, POINT_ONE, POINT_TWO, Globals::G_COLOUR_YELLOW, Globals::G_HOUGH_LINE_THICKNESS, cv::LINE_AA);
154 }
155 continue;
156 }
157
158 // ROI frame:
159 //
160 // Top edge of mask --> ____________________
161 // / / \ \
162 // Left edge of mask --> / / \ \
163 // / / \ \
164 // / # / @ \ & \
165 // / / \ \ <-- Right edge of mask
166 // / / \ \
167 // ---------- ----------- ---------- <-- Bottom edge of mask
168 // Left line threshold ^ ^ Right line threshold
169 //
170 // Any lines within:
171 // '#' region will be considered left lines
172 // '@' region will be considered middle lines
173 // '&' region will be considered right lines
174 //
175 // Within OpenCV, the origin is located at the top left of the frame. Thus, left lines should have negative
176 // gradients and right positive gradients.
177
178 // Remove the left edge of the ROI frame
181 if ((houghLine[Globals::G_VEC4_Y1_INDEX] <= LEFT_EDGE_Y1 + 1) && (houghLine[Globals::G_VEC4_Y2_INDEX] <= LEFT_EDGE_Y2 + 1)) // +1 for extra buffer
182 {
183 if (p_debugMode)
184 {
185 cv::line(m_laneDetectionInformation.m_houghLinesFrame, POINT_ONE, POINT_TWO, Globals::G_COLOUR_LIGHT_RED, Globals::G_HOUGH_LINE_THICKNESS, cv::LINE_AA);
186 }
187 continue;
188 }
189
190 // If within left threshold and has a negative gradient (meaning the line follows the expected path shown in the
191 // diagram above), then it is a left line
194 if ((houghLine[Globals::G_VEC4_Y1_INDEX] < LEFT_THRESHOLD_Y1) && (houghLine[Globals::G_VEC4_Y2_INDEX] < LEFT_THRESHOLD_Y2) && GRADIENT < 0)
195 {
196 m_leftLaneLines.push_back(houghLine);
197 m_leftLineAverageLength += std::sqrt(
198 ((houghLine[Globals::G_VEC4_X1_INDEX] - houghLine[Globals::G_VEC4_X2_INDEX]) * (houghLine[Globals::G_VEC4_X1_INDEX] - houghLine[Globals::G_VEC4_X2_INDEX])) +
199 ((houghLine[Globals::G_VEC4_Y1_INDEX] - houghLine[Globals::G_VEC4_Y2_INDEX]) * (houghLine[Globals::G_VEC4_Y1_INDEX] - houghLine[Globals::G_VEC4_Y2_INDEX])));
200
201 if (p_debugMode)
202 {
203 cv::line(m_laneDetectionInformation.m_houghLinesFrame, POINT_ONE, POINT_TWO, Globals::G_COLOUR_RED, Globals::G_HOUGH_LINE_THICKNESS, cv::LINE_AA);
204 }
205 continue;
206 }
207
208 // Remove the right edge of the ROI frame
211 if ((houghLine[Globals::G_VEC4_Y1_INDEX] <= RIGHT_EDGE_Y1 + 1) && (houghLine[Globals::G_VEC4_Y2_INDEX] <= RIGHT_EDGE_Y2 + 1)) // +1 for extra buffer
212 {
213 if (p_debugMode)
214 {
215 cv::line(m_laneDetectionInformation.m_houghLinesFrame, POINT_ONE, POINT_TWO, Globals::G_COLOUR_LIGHT_BLUE, Globals::G_HOUGH_LINE_THICKNESS, cv::LINE_AA);
216 }
217 continue;
218 }
219
220 // If within right threshold and has a positive gradient (meaning the line follows the expected path shown in the
221 // diagram above), then it is a right line
224 if ((houghLine[Globals::G_VEC4_Y1_INDEX] < RIGHT_THRESHOLD_Y1) && (houghLine[Globals::G_VEC4_Y2_INDEX] < RIGHT_THRESHOLD_Y2) && GRADIENT > 0)
225 {
226 m_rightLaneLines.push_back(houghLine);
227 m_rightLineAverageLength += std::sqrt(
228 ((houghLine[Globals::G_VEC4_X1_INDEX] - houghLine[Globals::G_VEC4_X2_INDEX]) * (houghLine[Globals::G_VEC4_X1_INDEX] - houghLine[Globals::G_VEC4_X2_INDEX])) +
229 ((houghLine[Globals::G_VEC4_Y1_INDEX] - houghLine[Globals::G_VEC4_Y2_INDEX]) * (houghLine[Globals::G_VEC4_Y1_INDEX] - houghLine[Globals::G_VEC4_Y2_INDEX])));
230
231 if (p_debugMode)
232 {
233 cv::line(m_laneDetectionInformation.m_houghLinesFrame, POINT_ONE, POINT_TWO, Globals::G_COLOUR_BLUE, Globals::G_HOUGH_LINE_THICKNESS, cv::LINE_AA);
234 }
235
236 continue;
237 }
238
239 // Otherwise the line is be a middle line
240 m_middleLaneLines.push_back(houghLine);
241 m_middleLineAverageLength += std::sqrt(
242 ((houghLine[Globals::G_VEC4_X1_INDEX] - houghLine[Globals::G_VEC4_X2_INDEX]) * (houghLine[Globals::G_VEC4_X1_INDEX] - houghLine[Globals::G_VEC4_X2_INDEX])) +
243 ((houghLine[Globals::G_VEC4_Y1_INDEX] - houghLine[Globals::G_VEC4_Y2_INDEX]) * (houghLine[Globals::G_VEC4_Y1_INDEX] - houghLine[Globals::G_VEC4_Y2_INDEX])));
244
245 if (p_debugMode)
246 {
247 cv::line(m_laneDetectionInformation.m_houghLinesFrame, POINT_ONE, POINT_TWO, Globals::G_COLOUR_GREEN, Globals::G_HOUGH_LINE_THICKNESS, cv::LINE_AA);
248 }
249 }
250
251 m_leftLineAverageLength /= static_cast<double>(m_leftLaneLines.size());
252 m_middleLineAverageLength /= static_cast<double>(m_middleLaneLines.size());
253 m_rightLineAverageLength /= static_cast<double>(m_rightLaneLines.size());
254 }
255
256 bool LaneDetector::IsLineWithinObjectBoundingBoxes(const cv::Vec4i& p_houghLine, const ObjectDetectionInformation& p_objectDetectionInformation)
257 {
258 for (const ObjectDetectionInformation::DetectedObjectInformation& detectedObjectInformation : p_objectDetectionInformation.m_objectInformation)
259 {
260 const int32_t LOWER_X = detectedObjectInformation.m_boundingBox.x;
261 const int32_t UPPER_X = detectedObjectInformation.m_boundingBox.x + detectedObjectInformation.m_boundingBox.width;
262 const int32_t LOWER_Y = detectedObjectInformation.m_boundingBox.y;
263 const int32_t UPPER_Y = detectedObjectInformation.m_boundingBox.y + detectedObjectInformation.m_boundingBox.height;
264
265 // The OR operator means that only whole line does not need to be within the bounding box for the line to be
266 // considered within the bounding boxes
267 if ((p_houghLine[Globals::G_VEC4_X1_INDEX] >= LOWER_X && p_houghLine[Globals::G_VEC4_X1_INDEX] <= UPPER_X &&
268 p_houghLine[Globals::G_VEC4_Y1_INDEX] >= LOWER_Y && p_houghLine[Globals::G_VEC4_Y1_INDEX] <= UPPER_Y) ||
269 (p_houghLine[Globals::G_VEC4_X2_INDEX] >= LOWER_X && p_houghLine[Globals::G_VEC4_X2_INDEX] <= UPPER_X &&
270 p_houghLine[Globals::G_VEC4_Y2_INDEX] >= LOWER_Y && p_houghLine[Globals::G_VEC4_Y2_INDEX] <= UPPER_Y))
271 {
272 return true;
273 }
274 }
275
276 return false;
277 }
278
280 {
281 m_laneDetectionInformation.m_drivingStateSubTitle = "(L = Solid )";
282 if (m_leftLaneLines.empty())
283 {
284 m_laneDetectionInformation.m_drivingStateSubTitle = "(L = Empty )";
285 }
286
288 {
289 m_laneDetectionInformation.m_drivingStateSubTitle = "(L = Dashed)";
290 }
291
292 std::string rightLaneLineState = " (R = Solid )";
293 if (m_rightLaneLines.empty())
294 {
295 rightLaneLineState = " (R = Empty )";
296 }
297
299 {
300 rightLaneLineState = " (R = Dashed)";
301 }
302
303 m_laneDetectionInformation.m_drivingStateSubTitle += rightLaneLineState;
304 }
305
307 {
308 // Driving state mapping:
309 //
310 // | Left Lane Lines | Middle Lane Lines | Right Lane Lines | Driving State |
311 // | --------------- |------------------ | ---------------- | -------------------------------- |
312 // | Detected | Detected | Detected | WITHIN_LANE |
313 // | Detected | | Detected | WITHIN_LANE |
314 // | | Detected | | CHANGING_LANES |
315 // | Detected | Detected | | CHANGING_LANES |
316 // | | Detected | Detected | CHANGING_LANES |
317 // | Detected | | | ONLY_LEFT_LANE_MARKING_DETECTED |
318 // | | | Detected | ONLY_RIGHT_LANE_MARKING_DETECTED |
319 // | | | | NO_LANE_MARKINGS_DETECTED |
320
321 const bool LEFT_LINES_PRESENT = !m_leftLaneLines.empty();
322 const bool MIDDLE_LINES_PRESENT = !m_middleLaneLines.empty();
323 const bool RIGHT_LINES_PRESNET = !m_rightLaneLines.empty();
324
325 Globals::DrivingState currentDrivingState = Globals::DrivingState::NO_LANE_MARKINGS_DETECTED;
326
327 if ((LEFT_LINES_PRESENT && MIDDLE_LINES_PRESENT && RIGHT_LINES_PRESNET) ||
328 (LEFT_LINES_PRESENT && !MIDDLE_LINES_PRESENT && RIGHT_LINES_PRESNET))
329 {
330 currentDrivingState = Globals::DrivingState::WITHIN_LANE;
331 }
332
333 else if ((!LEFT_LINES_PRESENT && MIDDLE_LINES_PRESENT && !RIGHT_LINES_PRESNET) ||
334 (LEFT_LINES_PRESENT && MIDDLE_LINES_PRESENT && !RIGHT_LINES_PRESNET) ||
335 (!LEFT_LINES_PRESENT && MIDDLE_LINES_PRESENT && RIGHT_LINES_PRESNET))
336 {
337 currentDrivingState = Globals::DrivingState::CHANGING_LANES;
338 }
339
340 else if (LEFT_LINES_PRESENT && !MIDDLE_LINES_PRESENT && !RIGHT_LINES_PRESNET)
341 {
342 currentDrivingState = Globals::DrivingState::ONLY_LEFT_LANE_MARKING_DETECTED;
343 }
344
345 else if (!LEFT_LINES_PRESENT && !MIDDLE_LINES_PRESENT && RIGHT_LINES_PRESNET)
346 {
347 currentDrivingState = Globals::DrivingState::ONLY_RIGHT_LANE_MARKING_DETECTED;
348 }
349
350 const Globals::DrivingState NEXT_DRIVING_STATE = m_drivingStateRollingAverage.CalculateRollingAverage(currentDrivingState);
351
352 // Check if the driving state will change
353 if (NEXT_DRIVING_STATE != m_currentDrivingState)
354 {
355 // If are now in CHANGING_LANES state
356 if (NEXT_DRIVING_STATE == Globals::DrivingState::CHANGING_LANES)
357 {
361 m_laneDetectionInformation.m_drivingStateSubTitle = "";
362 }
363 }
364
365 m_currentDrivingState = NEXT_DRIVING_STATE;
366 }
367
369 {
370 // Set titles based on driving state
372
373 m_laneDetectionInformation.m_laneOverlayCorners = {
374 {0, 0},
375 {0, 0},
376 {0, 0},
377 {0, 0}
378 };
379
380 switch (m_currentDrivingState)
381 {
382 case Globals::DrivingState::WITHIN_LANE:
384 break;
385
386 case Globals::DrivingState::CHANGING_LANES:
388 break;
389
390 case Globals::DrivingState::ONLY_LEFT_LANE_MARKING_DETECTED:
391 case Globals::DrivingState::ONLY_RIGHT_LANE_MARKING_DETECTED:
392 case Globals::DrivingState::NO_LANE_MARKINGS_DETECTED:
393 break;
394
395 default:
397 }
398 }
399
401 {
402 // Skip if either no left or right lane lines detected
403 if (m_leftLaneLines.empty() || m_rightLaneLines.empty())
404 {
405 return;
406 }
407
408 double leftLaneLineC = 0;
409 double leftLaneLineM = 0;
410 double leftLaneLineMinimumY = std::numeric_limits<double>::max();
411
412 for (const cv::Vec4i& leftLaneLine : m_leftLaneLines)
413 {
414 // Find the minimum height of a left lane line
415 leftLaneLineMinimumY = std::min({static_cast<double>(leftLaneLine[Globals::G_VEC4_Y1_INDEX]),
416 static_cast<double>(leftLaneLine[Globals::G_VEC4_Y2_INDEX]),
417 leftLaneLineMinimumY});
418
419 // Find average gradient using m = dy/dx
420 leftLaneLineM += (leftLaneLine[Globals::G_VEC4_Y1_INDEX] - leftLaneLine[Globals::G_VEC4_Y2_INDEX]) / static_cast<double>(leftLaneLine[Globals::G_VEC4_X1_INDEX] - leftLaneLine[Globals::G_VEC4_X2_INDEX]);
421 // Find average c using c = y - mx and just x1,y1 co-ordinates
422 leftLaneLineC += leftLaneLine[Globals::G_VEC4_Y1_INDEX] - ((leftLaneLine[Globals::G_VEC4_Y1_INDEX] - leftLaneLine[Globals::G_VEC4_Y2_INDEX]) / static_cast<double>(leftLaneLine[Globals::G_VEC4_X1_INDEX] - leftLaneLine[Globals::G_VEC4_X2_INDEX]) * leftLaneLine[Globals::G_VEC4_X1_INDEX]);
423 }
424
425 if (!m_leftLaneLines.empty())
426 {
427 leftLaneLineM /= static_cast<double>(m_leftLaneLines.size());
428 leftLaneLineC /= static_cast<double>(m_leftLaneLines.size());
429 }
430
431 // The same logic as above but for m_rightLaneLines
432 double rightLaneLineC = 0;
433 double rightLaneLineM = 0;
434 double rightLaneLineMinimumY = std::numeric_limits<double>::max();
435
436 for (const cv::Vec4i& rightLaneLine : m_rightLaneLines)
437 {
438 // Find the minimum height of a right lane line
439 rightLaneLineMinimumY = std::min({static_cast<double>(rightLaneLine[Globals::G_VEC4_Y1_INDEX]),
440 static_cast<double>(rightLaneLine[Globals::G_VEC4_Y2_INDEX]),
441 rightLaneLineMinimumY});
442
443 // Find average gradient using m = dy/dx
444 rightLaneLineM += (rightLaneLine[Globals::G_VEC4_Y1_INDEX] - rightLaneLine[Globals::G_VEC4_Y2_INDEX]) / static_cast<double>(rightLaneLine[Globals::G_VEC4_X1_INDEX] - rightLaneLine[Globals::G_VEC4_X2_INDEX]);
445 // Find average c using c = y - mx and just x1,y1 co-ordinates
446 rightLaneLineC += rightLaneLine[Globals::G_VEC4_Y1_INDEX] - ((rightLaneLine[Globals::G_VEC4_Y1_INDEX] - rightLaneLine[Globals::G_VEC4_Y2_INDEX]) / static_cast<double>(rightLaneLine[Globals::G_VEC4_X1_INDEX] - rightLaneLine[Globals::G_VEC4_X2_INDEX]) * rightLaneLine[Globals::G_VEC4_X1_INDEX]);
447 }
448
449 if (!m_rightLaneLines.empty())
450 {
451 rightLaneLineM /= static_cast<double>(m_rightLaneLines.size());
452 rightLaneLineC /= static_cast<double>(m_rightLaneLines.size());
453 }
454
455 // Find the minimum lane line height - due to OpenCV origin being located at the top left of the frame, visually this
456 // represents the maximum y value
457 const double LANE_LINE_MINIMUM_Y = std::min(leftLaneLineMinimumY, rightLaneLineMinimumY);
458
459 // Calculate the value y0 value for the intersection point of the left and right lane lines
460 //
461 // \ /
462 // \/ (x0, y0)
463 // /\
464 // / \
465 // / \
466 // / \
467 // / \
468 // / \
469 // ^ ^
470 // y0 = m1x0 + c1 y0 = m2x0 + c2
471 // x0 = (y0 - c1) / m1 x0 = (y0 - c2) / m2
472 //
473 // (y0 - c1) / m1 = (y0 - c2) / m2
474 // m2y0 - m2c1 = m1y0 - m1c2
475 // m2y0 - m1y0 = m2c1 - m1c2
476 // y0(m2 - m1) = m2c1 - m1c2
477 // y0 = (m2c1 - m1c2) / (m2 - m1)
478
479 const int32_t INTERSECTION_Y0 = static_cast<int32_t>((rightLaneLineM * leftLaneLineC - leftLaneLineM * rightLaneLineC) / (rightLaneLineM - leftLaneLineM));
480
481 // Only draw the lane overlay if the left and right lane lines intersect beyond the visible overlay. Otherwise they
482 // would intersect within the visible section and the overlay would turn in to an hourglass as shown above.
483 if (INTERSECTION_Y0 < LANE_LINE_MINIMUM_Y)
484 {
485 m_laneDetectionInformation.m_laneOverlayCorners[0] = cv::Point(static_cast<int32_t>((LANE_LINE_MINIMUM_Y - leftLaneLineC) / leftLaneLineM), static_cast<int32_t>(LANE_LINE_MINIMUM_Y)); // Top left
486 m_laneDetectionInformation.m_laneOverlayCorners[1] = cv::Point(static_cast<int32_t>((LANE_LINE_MINIMUM_Y - rightLaneLineC) / rightLaneLineM), static_cast<int32_t>(LANE_LINE_MINIMUM_Y)); // Top right
487 m_laneDetectionInformation.m_laneOverlayCorners[2] = cv::Point(static_cast<int32_t>((Globals::G_ROI_BOTTOM_HEIGHT - rightLaneLineC) / rightLaneLineM), Globals::G_ROI_BOTTOM_HEIGHT); // Bottom right
488 m_laneDetectionInformation.m_laneOverlayCorners[3] = cv::Point(static_cast<int32_t>((Globals::G_ROI_BOTTOM_HEIGHT - leftLaneLineC) / leftLaneLineM), Globals::G_ROI_BOTTOM_HEIGHT); // Bottom left
489 }
490 }
491
493 {
494 if (m_middleLaneLines.empty())
495 {
496 return;
497 }
498
499 double averageDistanceFromLeft = 0;
500 double averageDistanceFromRight = 0;
501
502 for (const cv::Vec4i& middleLaneLine : m_middleLaneLines)
503 {
504 // Determine X along the left/right edge of the mask using the Y co-ordinates of middleLaneLine and then use this X
505 // value to compare with the actual X co-ordinates of middleLaneLine to determine the distance.
506
507 const double LEFT_EDGE_X1 = (middleLaneLine[Globals::G_VEC4_Y1_INDEX] - Globals::G_LEFT_EDGE_OF_MASK_C) / Globals::G_LEFT_EDGE_OF_MASK_M;
508 const double LEFT_EDGE_X2 = (middleLaneLine[Globals::G_VEC4_Y2_INDEX] - Globals::G_LEFT_EDGE_OF_MASK_C) / Globals::G_LEFT_EDGE_OF_MASK_M;
509
510 averageDistanceFromLeft += std::fabs(middleLaneLine[Globals::G_VEC4_X1_INDEX] - LEFT_EDGE_X1);
511 averageDistanceFromLeft += std::fabs(middleLaneLine[Globals::G_VEC4_X2_INDEX] - LEFT_EDGE_X2);
512
513 const double RIGHT_EDGE_X1 = (middleLaneLine[Globals::G_VEC4_Y1_INDEX] - Globals::G_RIGHT_EDGE_OF_MASK_C) / Globals::G_RIGHT_EDGE_OF_MASK_M;
514 const double RIGHT_EDGE_X2 = (middleLaneLine[Globals::G_VEC4_Y2_INDEX] - Globals::G_RIGHT_EDGE_OF_MASK_C) / Globals::G_RIGHT_EDGE_OF_MASK_M;
515
516 averageDistanceFromRight += std::fabs(middleLaneLine[Globals::G_VEC4_X1_INDEX] - RIGHT_EDGE_X1);
517 averageDistanceFromRight += std::fabs(middleLaneLine[Globals::G_VEC4_X2_INDEX] - RIGHT_EDGE_X2);
518 }
519
520 averageDistanceFromLeft /= static_cast<double>(m_middleLaneLines.size() * 2);
521 averageDistanceFromRight /= static_cast<double>(m_middleLaneLines.size() * 2);
522
523 const double CURRENT_DISTANCE_DIFFERENCE = averageDistanceFromLeft - averageDistanceFromRight;
524
525 // There is no previous distance difference to compare to on the first frame
527 {
528 m_changingLanesPreviousDistanceDifference = CURRENT_DISTANCE_DIFFERENCE;
530 return;
531 }
532
533 // Allow for enough frames to pass to accurately determine a difference
535 {
536 const double CHANGE_IN_DISTANCE_DIFFERENCE = m_changingLanesPreviousDistanceDifference - CURRENT_DISTANCE_DIFFERENCE;
537
538 if (CHANGE_IN_DISTANCE_DIFFERENCE < 0)
539 {
540 m_laneDetectionInformation.m_drivingStateSubTitle = "(Currently Turning Left)";
541 }
542
543 else if (CHANGE_IN_DISTANCE_DIFFERENCE > 0)
544 {
545 m_laneDetectionInformation.m_drivingStateSubTitle = "(Currently Turning Right)";
546 }
547
548 else
549 {
550 m_laneDetectionInformation.m_drivingStateSubTitle = "(Currently Not Turning)";
551 }
552
553 m_changingLanesPreviousDistanceDifference = CURRENT_DISTANCE_DIFFERENCE;
554
556 }
557
559 }
560}
Used for functionality that has not been implemented.
Definition Globals.hpp:32
RollingAverage()
Constructs a new RollingAverage object.
std::deque< T > m_rollingAverageArray
A double-ended queue representing the states stored in the rolling average.
T CalculateRollingAverage(const T &p_nextInput)
Adds p_nextInput to the rolling average buffer and returns the most frequent value within the rolling...
LaneDetectionInformation GetInformation()
Get the LaneDetectionInformation struct.
void CalculateChangingLanesTurningDirection()
Determines the direction (left, right or neither) that the vehicle is turning while changing lanes.
Globals::DrivingState m_currentDrivingState
The current driving state.
static bool IsLineWithinObjectBoundingBoxes(const cv::Vec4i &p_houghLine, const ObjectDetectionInformation &p_objectDetectionInformation)
Determines whether p_houghLine is within any one of the p_objectDetectionInformation object bounding ...
std::vector< cv::Vec4i > m_middleLaneLines
The left, middle and right lane lines that have been detected for the current frame.
double m_middleLineAverageLength
The average left, middle and right lane line lengths for the current frame.
void AnalyseHoughLines(const std::vector< cv::Vec4i > &p_houghLines, const ObjectDetectionInformation &p_objectDetectionInformation, const bool &p_debugMode)
Determines whether the detected hough lines are left, middle or right lane lines. The majority of the...
void ExecuteDrivingState()
Execute logic based on the current driving state.
uint32_t m_changingLanesFrameCount
The number of frames passed since a distance difference was calculated while the vehicle is changing ...
LaneDetector()
Construct a new LaneDetector object.
bool m_changingLanesFirstFrame
Whether the current frame is the frame in which the driving state changed to CHANGING_LANES.
RollingAverage< Globals::DrivingState > m_drivingStateRollingAverage
The rolling average to determine the driving state.
std::vector< cv::Vec4i > m_rightLaneLines
The left, middle and right lane lines that have been detected for the current frame.
double m_rightLineAverageLength
The average left, middle and right lane line lengths for the current frame.
LaneDetectionInformation m_laneDetectionInformation
The LaneDetectionInformation struct containing all lane detection-related information.
void UpdateDrivingState()
Determines the current driving state depending upon the absence/presence of left, right and middle la...
double m_changingLanesPreviousDistanceDifference
The distance difference that will be compared to in order to determine the direction the vehicle is m...
void UpdateLineTypes()
Determines whether the left, middle, and right lane lines detected are solid, dashed or empty.
void RunLaneDetector(const cv::Mat &p_frame, const ObjectDetectionInformation &p_objectDetectionInformation, const bool &p_debugMode)
Runs the lane detector against p_frame.
double m_leftLineAverageLength
The average left, middle and right lane line lengths for the current frame.
std::vector< cv::Vec4i > m_leftLaneLines
The left, middle and right lane lines that have been detected for the current frame.
void CalculateLanePosition()
Determines the lane overlay for the current lane.
Contains all global and constant objects.
static const uint32_t G_SOLID_LINE_LENGTH_THRESHOLD
Threshold length to decide whether a line is to be considered solid line road marking.
Definition Globals.hpp:406
static const std::unordered_map< DrivingState, std::string > G_DRIVING_STATE_TITLES
The driving state titles to display.
Definition Globals.hpp:423
static const uint32_t G_CHANGING_LANES_DISTANCE_DIFFERENCE_FRAME_COUNT_THRESHOLD
The number of frames to wait before calculating another distance difference while the vehicle is chan...
Definition Globals.hpp:434
static const cv::Scalar G_COLOUR_LIGHT_BLUE
OpenCV Colours (in BGR format).
Definition Globals.hpp:154
static const cv::Range G_ROI_BOUNDING_BOX_Y_RANGE
The bounding box of the region-of-interest which is used to crop debugging frames....
Definition Globals.hpp:316
static const double G_RIGHT_LINE_THRESHOLD_M
Region-of-interest sub-division line equations (y = mx + c).
Definition Globals.hpp:365
static const uint32_t G_HOUGH_MIN_LINE_LENGTH
Hough transform threshold and properties.
Definition Globals.hpp:384
static const uint32_t G_VIDEO_INPUT_HEIGHT
Input video dimensions.
Definition Globals.hpp:103
static const uint32_t G_VIDEO_INPUT_WIDTH
Input video dimensions.
Definition Globals.hpp:104
static const double G_RIGHT_LINE_THRESHOLD_C
Region-of-interest sub-division line equations (y = mx + c).
Definition Globals.hpp:366
static const cv::Scalar G_COLOUR_GREY
OpenCV Colours (in BGR format).
Definition Globals.hpp:146
static const uint32_t G_VEC4_X1_INDEX
Hough line index mapping.
Definition Globals.hpp:392
static const cv::Range G_ROI_BOUNDING_BOX_X_RANGE
The bounding box of the region-of-interest which is used to crop debugging frames....
Definition Globals.hpp:315
static const int32_t G_HOUGH_LINE_THICKNESS
The thickness of the hough lines drawn in debug mode.
Definition Globals.hpp:250
static const cv::Scalar G_COLOUR_GREEN
OpenCV Colours (in BGR format).
Definition Globals.hpp:152
static const uint32_t G_VEC4_X2_INDEX
Hough line index mapping.
Definition Globals.hpp:394
static const cv::Scalar G_COLOUR_WHITE
OpenCV Colours (in BGR format).
Definition Globals.hpp:147
static const uint32_t G_VEC4_Y1_INDEX
Hough line index mapping.
Definition Globals.hpp:393
static const double G_DEBUGGING_FRAME_SCALING_FACTOR
Scaling factor for debugging frames.
Definition Globals.hpp:322
static const int32_t G_ROI_BOTTOM_HEIGHT
Region-of-interest dimensions.
Definition Globals.hpp:267
static const uint32_t G_HOUGH_MAX_LINE_GAP
Hough transform threshold and properties.
Definition Globals.hpp:385
static const uint32_t G_DEFAULT_ROLLING_AVERAGE_SIZE
Default rolling average size.
Definition Globals.hpp:260
static const double G_RIGHT_EDGE_OF_MASK_M
Region-of-interest sub-division line equations (y = mx + c).
Definition Globals.hpp:355
static const double G_LEFT_LINE_THRESHOLD_M
Region-of-interest sub-division line equations (y = mx + c).
Definition Globals.hpp:362
static const double G_LEFT_EDGE_OF_MASK_M
Region-of-interest sub-division line equations (y = mx + c).
Definition Globals.hpp:352
static const cv::Scalar G_COLOUR_YELLOW
OpenCV Colours (in BGR format).
Definition Globals.hpp:151
static const cv::Scalar G_COLOUR_RED
OpenCV Colours (in BGR format).
Definition Globals.hpp:148
static const double G_LEFT_EDGE_OF_MASK_C
Region-of-interest sub-division line equations (y = mx + c).
Definition Globals.hpp:353
static const uint32_t G_VEC4_Y2_INDEX
Hough line index mapping.
Definition Globals.hpp:395
static const std::array< cv::Point, G_NUMBER_OF_POINTS > G_ROI_MASK_POINTS
Region-of-interest points. Below is how the indicies of the array map to the points.
Definition Globals.hpp:287
static const double G_HOUGH_RHO
Hough transform threshold and properties.
Definition Globals.hpp:381
static const uint32_t G_HOUGH_THRESHOLD
Hough transform threshold and properties.
Definition Globals.hpp:383
static const double G_HOUGH_LINE_HORIZONTAL_GRADIENT_THRESHOLD
Threshold gradient to decide whether a line is to be considered horizontal.
Definition Globals.hpp:401
static const double G_RIGHT_EDGE_OF_MASK_C
Region-of-interest sub-division line equations (y = mx + c).
Definition Globals.hpp:356
DrivingState
The different driving states supported by the lane detector.
Definition Globals.hpp:412
static const double G_HOUGH_THETA
Hough transform threshold and properties.
Definition Globals.hpp:382
static const uint32_t G_CANNY_ALGORITHM_UPPER_THRESHOLD
Canny algorithm thresholds.
Definition Globals.hpp:374
static const cv::Scalar G_COLOUR_BLUE
OpenCV Colours (in BGR format).
Definition Globals.hpp:153
static const uint32_t G_CANNY_ALGORITHM_LOWER_THRESHOLD
Canny algorithm thresholds.
Definition Globals.hpp:373
static const cv::Scalar G_COLOUR_LIGHT_RED
OpenCV Colours (in BGR format).
Definition Globals.hpp:149
static const double G_LEFT_LINE_THRESHOLD_C
Region-of-interest sub-division line equations (y = mx + c).
Definition Globals.hpp:363
Contains all Lane-and-Object-Detection objects.
The information needed by FrameBuilder to update frame with lane detection information.
The information needed by FrameBuilder to update frame with object detection information.
std::vector< DetectedObjectInformation > m_objectInformation
The list of detected objects for the current frame.