Skip to content

Commit b1bad09

Browse files
author
Adamczyk Piotr
committed
Added lane detection
1 parent 290d66c commit b1bad09

File tree

7 files changed

+361
-0
lines changed

7 files changed

+361
-0
lines changed

.gitignore

Lines changed: 54 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8,3 +8,57 @@ VisualStudio/.vs/OpenCVHelloWorld/v16/.suo
88
.vs/VSWorkspaceState.json
99
.vs/slnx.sqlite
1010
.vs/SemiAutonomousLaneKeepingSystem/v16/.suo
11+
VisualStudio/LaneKeeping/x64/Debug/LaneDetection.obj
12+
VisualStudio/LaneKeeping/x64/Debug/LaneKeeping.log
13+
VisualStudio/LaneKeeping/x64/Debug/LaneKeeping.tlog/CL.command.1.tlog
14+
VisualStudio/LaneKeeping/x64/Debug/LaneKeeping.tlog/CL.read.1.tlog
15+
VisualStudio/LaneKeeping/x64/Debug/LaneKeeping.tlog/CL.write.1.tlog
16+
VisualStudio/LaneKeeping/x64/Debug/LaneKeeping.tlog/LaneKeeping.lastbuildstate
17+
VisualStudio/LaneKeeping/x64/Debug/LaneKeeping.tlog/link.command.1.tlog
18+
VisualStudio/LaneKeeping/x64/Debug/LaneKeeping.tlog/link.read.1.tlog
19+
VisualStudio/LaneKeeping/x64/Debug/LaneKeeping.tlog/link.write.1.tlog
20+
VisualStudio/LaneKeeping/x64/Debug/main.obj
21+
VisualStudio/LaneKeeping/x64/Debug/vc142.idb
22+
VisualStudio/LaneKeeping/x64/Debug/vc142.pdb
23+
VisualStudio/x64/Debug/LaneKeeping.exe
24+
VisualStudio/x64/Debug/LaneKeeping.ilk
25+
VisualStudio/x64/Debug/LaneKeeping.pdb
26+
VisualStudio/x64/Debug/opencv_annotation.exe
27+
VisualStudio/x64/Debug/opencv_interactive-calibration.exe
28+
VisualStudio/x64/Debug/opencv_version.exe
29+
VisualStudio/x64/Debug/opencv_version_win32.exe
30+
VisualStudio/x64/Debug/opencv_videoio_ffmpeg411_64.dll
31+
VisualStudio/x64/Debug/opencv_visualisation.exe
32+
VisualStudio/x64/Debug/opencv_world411.dll
33+
VisualStudio/x64/Debug/opencv_world411.pdb
34+
VisualStudio/x64/Debug/opencv_world411d.dll
35+
VisualStudio/x64/Debug/opencv_world411d.pdb
36+
VisualStudio/x64/Release/opencv_annotation.exe
37+
VisualStudio/x64/Release/opencv_interactive-calibration.exe
38+
VisualStudio/x64/Release/opencv_version.exe
39+
VisualStudio/x64/Release/opencv_version_win32.exe
40+
VisualStudio/x64/Release/opencv_videoio_ffmpeg411_64.dll
41+
VisualStudio/x64/Release/opencv_visualisation.exe
42+
VisualStudio/x64/Release/opencv_world411.dll
43+
VisualStudio/x64/Release/opencv_world411.pdb
44+
VisualStudio/x64/Release/opencv_world411d.dll
45+
VisualStudio/x64/Release/opencv_world411d.pdb
46+
VisualStudio/LaneKeeping/x64/Release/LaneDetection.obj
47+
VisualStudio/LaneKeeping/x64/Release/LaneKeeping.log
48+
VisualStudio/LaneKeeping/x64/Release/LaneKeeping.tlog/CL.command.1.tlog
49+
VisualStudio/LaneKeeping/x64/Release/LaneKeeping.tlog/CL.read.1.tlog
50+
VisualStudio/LaneKeeping/x64/Release/LaneKeeping.tlog/CL.write.1.tlog
51+
VisualStudio/LaneKeeping/x64/Release/LaneKeeping.tlog/LaneKeeping.lastbuildstate
52+
VisualStudio/LaneKeeping/x64/Release/LaneKeeping.tlog/LaneKeeping.write.1u.tlog
53+
VisualStudio/LaneKeeping/x64/Release/LaneKeeping.tlog/link.command.1.tlog
54+
VisualStudio/LaneKeeping/x64/Release/LaneKeeping.tlog/link.read.1.tlog
55+
VisualStudio/LaneKeeping/x64/Release/LaneKeeping.tlog/link.write.1.tlog
56+
VisualStudio/LaneKeeping/x64/Release/main.obj
57+
VisualStudio/LaneKeeping/x64/Release/vc142.pdb
58+
VisualStudio/x64/Release/LaneKeeping.exe
59+
VisualStudio/x64/Release/LaneKeeping.iobj
60+
VisualStudio/x64/Release/LaneKeeping.ipdb
61+
VisualStudio/x64/Release/LaneKeeping.pdb
62+
VisualStudio/LaneKeeping/x64/Debug/LaneDetector.obj
63+
VisualStudio/LaneKeeping/x64/Release/LaneDetector.obj
64+
VisualStudio/LaneKeeping/x64/Release/LaneKeeping.tlog/link.delete.1.tlog
Lines changed: 182 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,182 @@
1+
#include "LaneDetection.h"
2+
3+
cv::Mat LaneDetection::s_frame;
4+
int LaneDetection::s_frameCenter;
5+
int LaneDetection::s_maxLineHeight;
6+
cv::Mat LaneDetection::s_mask;
7+
std::vector<cv::Vec4i> LaneDetection::s_lines;
8+
std::vector<cv::Point> LaneDetection::s_rightLinePoints;
9+
std::vector<cv::Point> LaneDetection::s_leftLinePoints;
10+
std::array<cv::Point, 4> LaneDetection::s_boundaries;
11+
12+
void LaneDetection::createMask(const cv::Size& frameSize, double frameFormat) {
13+
s_mask = cv::Mat::zeros(frameSize, frameFormat);
14+
15+
const float hScale = 0.625;
16+
17+
std::array<cv::Point, 4> points ({
18+
cv::Point(0.08 * frameSize.width, frameSize.height),
19+
cv::Point(0.42 * frameSize.width, hScale * frameSize.height),
20+
cv::Point(0.56 * frameSize.width, hScale * frameSize.height),
21+
cv::Point( frameSize.width, frameSize.height)
22+
});
23+
24+
//Create trapezoid mask
25+
cv::fillConvexPoly(s_mask, points.data(), 4, cv::Scalar(255, 0, 0));
26+
}
27+
28+
inline void LaneDetection::applyMask() {
29+
cv::bitwise_and(s_frame, s_mask, s_frame);
30+
}
31+
32+
inline void LaneDetection::blur() {
33+
cv::GaussianBlur(s_frame, s_frame, cv::Size(3, 3), 0, 0); //3x3px trial & error
34+
}
35+
36+
inline void LaneDetection::edgeDetection() {
37+
38+
cv::cvtColor(s_frame, s_frame, cv::COLOR_RGB2GRAY);
39+
40+
//binarize gray image
41+
cv::threshold(s_frame, s_frame, 140, 255, cv::THRESH_BINARY); //threshold trial & error
42+
43+
/*
44+
45+
Create the kernel [-1 0 1]
46+
This kernel is based on the one found in the
47+
Lane Departure Warning System by Mathworks
48+
49+
*/
50+
cv::Point anchor = cv::Point(-1, -1);
51+
cv::Mat kernel = cv::Mat(1, 3, CV_32F);
52+
kernel.at<float>(0, 0) = -1;
53+
kernel.at<float>(0, 1) = 0;
54+
kernel.at<float>(0, 2) = 1;
55+
56+
//filter the binary image to obtain the edges //compare results to CannyEdge??
57+
cv::filter2D(s_frame, s_frame, -1, kernel, anchor, 0, cv::BORDER_DEFAULT);
58+
}
59+
60+
inline void LaneDetection::houghLines() {
61+
s_lines.clear();
62+
63+
//calibrate once every x seconds?
64+
HoughLinesP(s_frame, s_lines, 1, CV_PI / 180, 20, 20, 30); //rho & theta by trial & error
65+
}
66+
67+
void LaneDetection::classifyLines() {
68+
s_rightLinePoints.clear();
69+
s_leftLinePoints.clear();
70+
71+
const float minSlope = 0.3f;
72+
const float maxSlope = 1.5f;
73+
74+
for (const auto& line : s_lines) {
75+
76+
//slope = (y1 - y0) / (x1 - x0)
77+
float slope = static_cast<float>(line[3] - line[1]);
78+
slope /= ( static_cast<float>(line[2] - line[0]) + 0.00001f );
79+
80+
//filter too horizontal slopes
81+
float absSlope = std::fabs(slope);
82+
if (absSlope < minSlope || absSlope > maxSlope) continue;
83+
84+
if (slope > 0 && line[2] > s_frameCenter && line[0] > s_frameCenter) {
85+
s_rightLinePoints.push_back(cv::Point(line[0], line[1]));
86+
s_rightLinePoints.push_back(cv::Point(line[2], line[3]));
87+
} else if (slope < 0 && line[2] < s_frameCenter && line[0] < s_frameCenter) {
88+
s_leftLinePoints.push_back(cv::Point(line[0], line[1]));
89+
s_leftLinePoints.push_back(cv::Point(line[2], line[3]));
90+
}
91+
}
92+
}
93+
94+
void LaneDetection::leastSquaresRegression() {
95+
96+
/*
97+
if left/right lane is not detected previous value is reused
98+
issue: if at least 1 lane is not detected in 1st frame => garbage output
99+
100+
TODO: averaging over few frames
101+
*/
102+
103+
//fit right lane
104+
if (!s_rightLinePoints.empty()) {
105+
cv::Vec4d right_line;
106+
107+
cv::fitLine(s_rightLinePoints, right_line, cv::DIST_L2, 0, 0.01, 0.01);
108+
float right_m = right_line[1] / right_line[0];
109+
cv::Point right_b = cv::Point(right_line[2], right_line[3]); // y = m*x + b
110+
111+
float right_ini_x = (static_cast<float>(s_frame.rows - right_b.y) / right_m) + right_b.x;
112+
float right_fin_x = (static_cast<float>(s_maxLineHeight - right_b.y) / right_m) + right_b.x;
113+
s_boundaries[0] = cv::Point(right_ini_x, s_frame.rows);
114+
s_boundaries[1] = cv::Point(right_fin_x, s_maxLineHeight);
115+
}
116+
117+
//fit left lane
118+
if (!s_leftLinePoints.empty()) {
119+
cv::Vec4d left_line;
120+
121+
cv::fitLine(s_leftLinePoints , left_line, cv::DIST_L2, 0, 0.01, 0.01);
122+
float left_m = left_line[1] / left_line[0];
123+
cv::Point left_b = cv::Point(left_line[2], left_line[3]);
124+
125+
float left_ini_x = (static_cast<float>(s_frame.rows - left_b.y) / left_m) + left_b.x;
126+
float left_fin_x = (static_cast<float>(s_maxLineHeight - left_b.y) / left_m) + left_b.x;
127+
s_boundaries[2] = cv::Point(left_ini_x, s_frame.rows);
128+
s_boundaries[3] = cv::Point(left_fin_x, s_maxLineHeight);
129+
}
130+
}
131+
132+
void LaneDetection::prepare(const cv::Size& frameSize, double frameFormat) {
133+
createMask(frameSize, frameFormat);
134+
s_frameCenter = frameSize.width / 2;
135+
s_maxLineHeight = static_cast<int>(0.66f * frameSize.height);
136+
}
137+
138+
void LaneDetection::setFrame(const cv::Mat& frame) {
139+
s_frame = frame;
140+
}
141+
142+
void LaneDetection::process(cv::Mat& frame) {
143+
setFrame(frame);
144+
145+
blur(); //remove noise by blurring image
146+
edgeDetection(); //detect edges
147+
148+
applyMask(); //crop ROI
149+
houghLines(); // use HoughLinesP
150+
151+
if (!s_lines.empty()) {
152+
classifyLines(); //Classify which lines are for left or right lane
153+
leastSquaresRegression(); //calculate lane regression
154+
155+
display(frame);
156+
}
157+
}
158+
159+
void LaneDetection::display(cv::Mat& frame) {
160+
161+
cv::Mat output;
162+
frame.copyTo(output);
163+
164+
//create semi-transparent trapezoid
165+
std::array<cv::Point, 4> poly_points;
166+
poly_points[0] = s_boundaries[2];
167+
poly_points[1] = s_boundaries[0];
168+
poly_points[2] = s_boundaries[1];
169+
poly_points[3] = s_boundaries[3];
170+
171+
cv::fillConvexPoly(output, poly_points.data(), 4, cv::Scalar(255, 255, 255), cv::LINE_AA, 0);
172+
cv::addWeighted(output, 0.4, frame, 0.6, 0, frame);
173+
174+
//draw left & right lane
175+
cv::line(frame, s_boundaries[0], s_boundaries[1], cv::Scalar(255, 255, 255), 7, cv::LINE_AA);
176+
cv::line(frame, s_boundaries[2], s_boundaries[3], cv::Scalar(255, 255, 255), 7, cv::LINE_AA);
177+
178+
//display processed frame
179+
cv::imshow("Lane detection", frame);
180+
cv::waitKey(1);
181+
}
182+
Lines changed: 36 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,36 @@
1+
#pragma once
2+
#include <opencv2/opencv.hpp>
3+
#include <vector>
4+
#include <array>
5+
6+
class LaneDetection {
7+
8+
static cv::Mat s_frame; //Current frame
9+
static int s_frameCenter;
10+
static int s_maxLineHeight;
11+
12+
static cv::Mat s_mask;
13+
static std::vector<cv::Vec4i> s_lines;
14+
15+
static std::vector<cv::Point> s_rightLinePoints;
16+
static std::vector<cv::Point> s_leftLinePoints;
17+
static std::array<cv::Point, 4> s_boundaries;
18+
19+
static void createMask(const cv::Size& frameSize, double frameFormat);
20+
static inline void applyMask();
21+
static inline void blur();
22+
static inline void edgeDetection();
23+
static inline void houghLines();
24+
static void classifyLines();
25+
static void leastSquaresRegression();
26+
27+
28+
public:
29+
static void prepare(const cv::Size& frameSize, double frameFormat);
30+
static void setFrame(const cv::Mat& frame);
31+
static void process(cv::Mat& frame);
32+
static void display(cv::Mat& frame);
33+
34+
35+
};
36+

VisualStudio/LaneKeeping/LaneKeeping.vcxproj

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -92,6 +92,7 @@
9292
<ConformanceMode>true</ConformanceMode>
9393
<AdditionalIncludeDirectories>C:\opencv\build\include;%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>
9494
<AdditionalOptions>-D WITH_CUDA=OFF %(AdditionalOptions)</AdditionalOptions>
95+
<LanguageStandard>stdcpp17</LanguageStandard>
9596
</ClCompile>
9697
<Link>
9798
<SubSystem>Console</SubSystem>
@@ -126,6 +127,7 @@
126127
<SDLCheck>true</SDLCheck>
127128
<ConformanceMode>true</ConformanceMode>
128129
<AdditionalIncludeDirectories>C:\opencv\build\include;%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>
130+
<LanguageStandard>stdcpp17</LanguageStandard>
129131
</ClCompile>
130132
<Link>
131133
<SubSystem>Console</SubSystem>
@@ -136,8 +138,12 @@
136138
</Link>
137139
</ItemDefinitionGroup>
138140
<ItemGroup>
141+
<ClCompile Include="LaneDetection.cpp" />
142+
<ClCompile Include="main.cpp" />
139143
</ItemGroup>
140144
<ItemGroup>
145+
<ClInclude Include="LaneDetection.h" />
146+
<ClInclude Include="Timer.h" />
141147
</ItemGroup>
142148
<Import Project="$(VCTargetsPath)\Microsoft.Cpp.targets" />
143149
<ImportGroup Label="ExtensionTargets">

VisualStudio/LaneKeeping/LaneKeeping.vcxproj.filters

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -15,7 +15,19 @@
1515
</Filter>
1616
</ItemGroup>
1717
<ItemGroup>
18+
<ClCompile Include="LaneDetection.cpp">
19+
<Filter>Pliki źródłowe</Filter>
20+
</ClCompile>
21+
<ClCompile Include="main.cpp">
22+
<Filter>Pliki źródłowe</Filter>
23+
</ClCompile>
1824
</ItemGroup>
1925
<ItemGroup>
26+
<ClInclude Include="LaneDetection.h">
27+
<Filter>Pliki nagłówkowe</Filter>
28+
</ClInclude>
29+
<ClInclude Include="Timer.h">
30+
<Filter>Pliki nagłówkowe</Filter>
31+
</ClInclude>
2032
</ItemGroup>
2133
</Project>

VisualStudio/LaneKeeping/Timer.h

Lines changed: 35 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,35 @@
1+
#pragma once
2+
#include <chrono>
3+
#include <string_view>
4+
#include <iostream>
5+
6+
class Timer {
7+
8+
public:
9+
Timer(std::string_view title, long long scaler = 1) {
10+
m_startPoint = std::chrono::high_resolution_clock::now();
11+
m_title = title;
12+
m_scaler = scaler;
13+
}
14+
15+
~Timer() {
16+
std::chrono::time_point<std::chrono::high_resolution_clock> endPoint = std::chrono::high_resolution_clock::now();
17+
auto start = std::chrono::time_point_cast<std::chrono::microseconds>(m_startPoint).time_since_epoch().count();
18+
auto end = std::chrono::time_point_cast<std::chrono::microseconds>(endPoint).time_since_epoch().count();
19+
20+
auto duration = end - start;
21+
if (m_scaler != 1) {
22+
std::cout << "On average ";
23+
duration /= m_scaler;
24+
}
25+
auto ms = duration * 0.001;
26+
27+
std::cout << m_title << " took " << duration << " us (" << ms << " ms)\n";
28+
}
29+
30+
private:
31+
std::string_view m_title;
32+
std::chrono::time_point<std::chrono::high_resolution_clock> m_startPoint;
33+
long long m_scaler;
34+
};
35+

0 commit comments

Comments
 (0)