// Author: Francis Liu
// Email: francislucien2017@126.com
// This code is about lane detection by using OpenCV3.
#include<cstdio>
#include<cmath>
#include<iostream>
#include<vector>
#include<opencv2/opencv.hpp>
#include<opencv2/core/core.hpp>
#include<opencv2/highgui/highgui.hpp>
#include<opencv2/imgproc/imgproc.hpp>
using std::cout;
typedef std::vector<cv::Vec4i> linesType;
const double PI = 3.1415926535;
bool show_steps = false;
// m: 斜率, b: 截距, norm: 线长
struct hough_pts {
double m, b, norm;
hough_pts(double m, double b, double norm) :
m(m), b(b), norm(norm) {};
};
void getROI(cv::Mat img, std::vector<cv::Point2i> vertices, cv::Mat& masked) {
cv::Mat mask = cv::Mat::zeros(img.size(), img.type());
if (img.channels() == 1) {
cv::fillConvexPoly(mask, vertices, cv::Scalar(255));
}
else if(img.channels() == 3){
cv::fillConvexPoly(mask, vertices, cv::Scalar(255,255,255));
}
int a = 10;
cv::bitwise_and(img, img, masked, mask);
}
void drawLines(cv::Mat& img, linesType lines, cv::Scalar color) {
for (int i = 0; i < lines.size(); ++i) {
cv::Point pt1 = cv::Point(lines[i][0], lines[i][1]);
cv::Point pt2 = cv::Point(lines[i][2], lines[i][3]);
cv::line(img, pt1, pt2, color, 4, 8);
}
}
void averageLines(cv::Mat, linesType lines, double y_min, double y_max, linesType& output) {
std::vector<hough_pts> left_lane, right_lane;
for (int i = 0; i < lines.size(); ++i) {
cv::Point2f pt1 = cv::Point2f(lines[i][0], lines[i][1]);
cv::Point2f pt2 = cv::Point2f(lines[i][2], lines[i][3]);
double m = (pt2.y - pt1.y) / (pt2.x - pt1.x);
double b = -m * pt1.x + pt1.y;
double norm = sqrt((pt2.x - pt1.x)*(pt2.x - pt1.x) + (pt2.y - pt1.y)*(pt2.y - pt1.y));
if (m < 0) { // left lane
left_lane.push_back(hough_pts(m, b, norm));
}
if (m > 0) { // right lane
right_lane.push_back(hough_pts(m, b, norm));
}
}
double b_avg_left = 0.0, m_avg_left = 0.0, xmin_left, xmax_left;
for (int i = 0; i < left_lane.size(); ++i) {
b_avg_left += left_lane[i].b;
m_avg_left += left_lane[i].m;
}
b_avg_left /= left_lane.size();
m_avg_left /= left_lane.size();
xmin_left = int((y_min - b_avg_left) / m_avg_left);
xmax_left = int((y_max - b_avg_left) / m_avg_left);
// left lane
output.push_back(cv::Vec4i(xmin_left, y_min, xmax_left, y_max));
double b_avg_right = 0.0, m_avg_right = 0.0, xmin_right, xmax_right;
for (int i = 0; i < right_lane.size(); ++i) {
b_avg_right += right_lane[i].b;
m_avg_right += right_lane[i].m;
}
b_avg_right /= right_lane.size();
m_avg_right /= right_lane.size();
xmin_right = int((y_min - b_avg_right) / m_avg_right);
xmax_right = int((y_max - b_avg_right) / m_avg_right);
// right lane
output.push_back(cv::Vec4i(xmin_right, y_min, xmax_right, y_max));
// left lane: output[0]
// right lane: output[1]
}
void bypassAngleFilter(linesType lines, double low_thres, double high_thres, linesType& output) {
for (int i = 0; i < lines.size(); ++i) {
double x1 = lines[i][0], y1 = lines[i][1];
double x2 = lines[i][2], y2 = lines[i][3];
double angle = abs(atan((y2 - y1) / (x2 - x1)) * 180 / PI);
if (angle > low_thres && angle < high_thres) {
output.push_back(cv::Vec4i(x1, y1, x2, y2));
}
}
}
void yellowEnhance(cv::Mat img_rgb, cv::Mat& result) {
cv::Mat img_hsv;
cv::cvtColor(img_rgb, img_hsv, cv::COLOR_RGB2HSV);
cv::Scalar lower_yellow = cv::Scalar(40, 100, 20);
cv::Scalar upper_yellow = cv::Scalar(100, 255, 255);
cv::Mat mask;
// inRange 之内为 255, 否则为 0
cv::inRange(img_hsv, lower_yellow, upper_yellow, mask);
cv::Mat gray;
cv::cvtColor(img_rgb, gray, cv::COLOR_BGR2GRAY);
cv::addWeighted(mask, 1.0, gray, 1.0, 1.0, result);
}
void pipeline(cv::Mat img, std::vector<cv::Point>vertices, double low_thres, double high_thres, cv::Mat& img_all_lines) {
cv::Mat gray;
yellowEnhance(img, gray);
cv::Mat gray_blur;
cv::GaussianBlur(gray, gray_blur, cv::Size(3, 3), 0, 0);
cv::Mat element = cv::getStructuringElement(cv::MORPH_RECT, cv::Size(3, 3));
cv::morphologyEx(gray_blur, gray_blur, cv::MORPH_DILATE, element, cv::Point(-1, -1), 3);
cv::Mat edges;
cv::Canny(gray_blur, edges, 50, 180);
cv::Mat masked;
getROI(edges, vertices, masked);
std::vector<cv::Vec4i>lines;
cv::HoughLinesP(masked, lines, 1, CV_PI / 180, 26, 5, 50);
cv::Mat hlines_img = cv::Mat::zeros(img.size(), CV_8UC3);
drawLines(hlines_img, lines, cv::Scalar(255, 0, 0));
linesType filtered_lines;
bypassAngleFilter(lines, low_thres, high_thres, filtered_lines);
cv::Mat avg_img = cv::Mat::zeros(img.size(), CV_8UC3);
linesType avg_hlines;
averageLines(img, filtered_lines, int(img.rows * 0.65), img.rows, avg_hlines);
drawLines(avg_img, avg_hlines, cv::Scalar(255, 0, 0));
if (show_steps) {
cv::imshow("original", img);
cv::imshow("gray", gray);
cv::imshow("gray_blur", gray_blur);
cv::imshow("edges", edges);
cv::imshow("masked image", masked);
cv::imshow("Hough Lines image", hlines_img);
cv::waitKey(0);
}
cv::Mat colorMask = cv::Mat::zeros(img.size(), CV_8UC3);
std::vector<cv::Point2i> lineVertices;
lineVertices.push_back(cv::Point2i(avg_hlines[0][0], avg_hlines[0][1]));
lineVertices.push_back(cv::Point2i(avg_hlines[0][2], avg_hlines[0][3]));
lineVertices.push_back(cv::Point2i(avg_hlines[1][2], avg_hlines[1][3]));
lineVertices.push_back(cv::Point2i(avg_hlines[1][0], avg_hlines[1][1]));
cv::fillConvexPoly(colorMask, lineVertices, cv::Scalar(0, 255, 0));
cv::addWeighted(avg_img, 1.0, img, 1, 0.0, img_all_lines);
cv::addWeighted(img_all_lines, 1.0, colorMask, 0.6, 0.0, img_all_lines);
}
void processImg(cv::Mat img, cv::Mat& result, int h, int w) {
cv::Size size = cv::Size(h, w);
std::vector<cv::Point2i> vertices;
vertices.push_back(cv::Point2i(200, size.width - 80));
vertices.push_back(cv::Point2i(490, int(size.width*0.65)));
vertices.push_back(cv::Point2i(820, int(size.width*0.65)));
vertices.push_back(cv::Point2i(size.height - 150, size.width - 80));
vertices.push_back(cv::Point2i(100, size.width - 80));
double low_thres = 30.0;
double high_thres = 80.0;
pipeline(img, vertices, low_thres, high_thres, result);
}
int main() {
std::string dir_video = "test_videos/";
std::string challenge_output = dir_video + "detection.avi";
cv::VideoCapture cap(dir_video + "solidWhiteRight.mp4");
//cv::VideoWriter writer;
const int fps = 30;
const int width = cap.get(cv::CAP_PROP_FRAME_WIDTH);
const int height = cap.get(cv::CAP_PROP_FRAME_HEIGHT);
cv::Size size(width, height);
//writer.open(challenge_output, CV_FOURCC('M', 'J', 'P', 'G'), fps, size);
while (1) {
cv::Mat frame;
cap >> frame;
if (frame.empty()) break;
cv::Mat result;
processImg(frame, result, height, width);
//writer << result;
cv::imshow("result", result);
cv::waitKey(30);
}
cap.release();
return 0;
}
评论6