本篇文章为大家展示了怎么在C++中使用opencv实现一个车道线识别功能,内容简明扼要并且容易理解,绝对能使你眼前一亮,通过这篇文章的详细介绍希望你能有所收获。
(一)目前国内外广泛使用的车道线检测方法主要分为两大类:
(1) 基于道路特征的车道线检测;
(2) 基于道路模型的车道线检测。
基于道路特征的车道线检测作为主流检测方法之一,主要是利用车道线与道路环境的物理特征差异进行后续图像的分割与处理,从而突出车道线特征,以实现车道线的检测。该方法复杂度较低,实时性较高,但容易受到道路环境干扰。
基于道路模型的车道线检测主要是基于不同的二维或三维道路图像模型(如直线型、抛物线型、样条曲线型、组合模型等) ,采用相应方法确定各模型参数,然后进行车道线拟合。该方法对特定道路的检测具有较高的准确度,但局限性强、运算量大、实时性较差。
(二)在这我介绍一种车道线检测方法,效果在高速上还可以,对于破损道路,光照变化太大等道路效果不佳,后续继续改进(直方图均衡以及多特征融合等等),这里有个基础版本的接口,大致步骤如下
(1)图像灰度化
(2)图像高斯滤波
(3)边缘检测
(4)获取掩膜,获取感兴趣区域
(5)霍夫变换检测直线
(6)将检测到的车道线分类,设置阈值,以图像中线分为左右两边的车道线,存入一个vector
(7)回归两条直线,即左右分别两个点,且求出斜率方程
(8)确定车道线的转弯与否
下面我贴出代码
(1)头文件(LaneDetector.h)
class LaneDetector {private: double img_size; double img_center; bool left_flag = false; // Tells us if there's left boundary of lane detected bool right_flag = false; // Tells us if there's right boundary of lane detected cv::Point right_b; // Members of both line equations of the lane boundaries: double right_m; // y = m*x + b cv::Point left_b; // double left_m; //public: cv::Mat deNoise(cv::Mat inputImage); // Apply Gaussian blurring to the input Image cv::Mat edgeDetector(cv::Mat img_noise); // Filter the image to obtain only edges cv::Mat mask(cv::Mat img_edges); // Mask the edges image to only care about ROI std::vector<cv::Vec4i> houghLines(cv::Mat img_mask); // Detect Hough lines in masked edges image std::vector<std::vector<cv::Vec4i> > lineSeparation(std::vector<cv::Vec4i> lines, cv::Mat img_edges); // Sprt detected lines by their slope into right and left lines std::vector<cv::Point> regression(std::vector<std::vector<cv::Vec4i> > left_right_lines, cv::Mat inputImage); // Get only one line for each side of the lane std::string predictTurn(); // Determine if the lane is turning or not by calculating the position of the vanishing point int plotLane(cv::Mat inputImage, std::vector<cv::Point> lane, std::string turn); // Plot the resultant lane and turn prediction in the frame.};
源文件LaneDetector.cpp
*@file LaneDetector.cpp*@author Miguel Maestre Trueba*@brief Definition of all the function that form part of the LaneDetector class.*@brief The class will take RGB images as inputs and will output the same RGB image but*@brief with the plot of the detected lanes and the turn prediction.*/#include <string>#include <vector>#include <opencv2/opencv.hpp>#include "LaneDetector.h"// IMAGE BLURRINGcv::Mat LaneDetector::deNoise(cv::Mat inputImage) { cv::Mat output; cv::GaussianBlur(inputImage, output, cv::Size(3, 3), 0, 0); return output;}// EDGE DETECTIONcv::Mat LaneDetector::edgeDetector(cv::Mat img_noise) { cv::Mat output; cv::Mat kernel; cv::Point anchor; // Convert image from RGB to gray cv::cvtColor(img_noise, output, cv::COLOR_RGB2GRAY); // Binarize gray image cv::threshold(output, output, 140, 255, cv::THRESH_BINARY); // Create the kernel [-1 0 1] // This kernel is based on the one found in the // Lane Departure Warning System by Mathworks anchor = cv::Point(-1, -1); kernel = cv::Mat(1, 3, CV_32F); kernel.at<float>(0, 0) = -1; kernel.at<float>(0, 1) = 0; kernel.at<float>(0, 2) = 1; // Filter the binary image to obtain the edges cv::filter2D(output, output, -1, kernel, anchor, 0, cv::BORDER_DEFAULT); cv::imshow("output", output); return output;}// MASK THE EDGE IMAGEcv::Mat LaneDetector::mask(cv::Mat img_edges) { cv::Mat output; cv::Mat mask = cv::Mat::zeros(img_edges.size(), img_edges.type()); cv::Point pts[4] = { cv::Point(210, 720), cv::Point(550, 450), cv::Point(717, 450), cv::Point(1280, 720) }; // Create a binary polygon mask cv::fillConvexPoly(mask, pts, 4, cv::Scalar(255, 0, 0)); // Multiply the edges image and the mask to get the output cv::bitwise_and(img_edges, mask, output); return output;}// HOUGH LINESstd::vector<cv::Vec4i> LaneDetector::houghLines(cv::Mat img_mask) { std::vector<cv::Vec4i> line; // rho and theta are selected by trial and error HoughLinesP(img_mask, line, 1, CV_PI / 180, 20, 20, 30); return line;}// SORT RIGHT AND LEFT LINESstd::vector<std::vector<cv::Vec4i> > LaneDetector::lineSeparation(std::vector<cv::Vec4i> lines, cv::Mat img_edges) { std::vector<std::vector<cv::Vec4i> > output(2); size_t j = 0; cv::Point ini; cv::Point fini; double slope_thresh = 0.3; std::vector<double> slopes; std::vector<cv::Vec4i> selected_lines; std::vector<cv::Vec4i> right_lines, left_lines; // Calculate the slope of all the detected lines for (auto i : lines) { ini = cv::Point(i[0], i[1]); fini = cv::Point(i[2], i[3]); // Basic algebra: slope = (y1 - y0)/(x1 - x0) double slope = (static_cast<double>(fini.y) - static_cast<double>(ini.y)) / (static_cast<double>(fini.x) - static_cast<double>(ini.x) + 0.00001); // If the slope is too horizontal, discard the line // If not, save them and their respective slope if (std::abs(slope) > slope_thresh) { slopes.push_back(slope); selected_lines.push_back(i); } } // Split the lines into right and left lines img_center = static_cast<double>((img_edges.cols / 2)); while (j < selected_lines.size()) { ini = cv::Point(selected_lines[j][0], selected_lines[j][1]); fini = cv::Point(selected_lines[j][2], selected_lines[j][3]); // Condition to classify line as left side or right side if (slopes[j] > 0 && fini.x > img_center && ini.x > img_center) { right_lines.push_back(selected_lines[j]); right_flag = true; } else if (slopes[j] < 0 && fini.x < img_center && ini.x < img_center) { left_lines.push_back(selected_lines[j]); left_flag = true; } j++; } output[0] = right_lines; output[1] = left_lines; return output;}// REGRESSION FOR LEFT AND RIGHT LINESstd::vector<cv::Point> LaneDetector::regression(std::vector<std::vector<cv::Vec4i> > left_right_lines, cv::Mat inputImage) { std::vector<cv::Point> output(4); cv::Point ini; cv::Point fini; cv::Point ini2; cv::Point fini2; cv::Vec4d right_line; cv::Vec4d left_line; std::vector<cv::Point> right_pts; std::vector<cv::Point> left_pts; // If right lines are being detected, fit a line using all the init and final points of the lines if (right_flag == true) { for (auto i : left_right_lines[0]) { ini = cv::Point(i[0], i[1]); fini = cv::Point(i[2], i[3]); right_pts.push_back(ini); right_pts.push_back(fini); } if (right_pts.size() > 0) { // The right line is formed here cv::fitLine(right_pts, right_line, CV_DIST_L2, 0, 0.01, 0.01); right_m = right_line[1] / right_line[0]; right_b = cv::Point(right_line[2], right_line[3]); } } // If left lines are being detected, fit a line using all the init and final points of the lines if (left_flag == true) { for (auto j : left_right_lines[1]) { ini2 = cv::Point(j[0], j[1]); fini2 = cv::Point(j[2], j[3]); left_pts.push_back(ini2); left_pts.push_back(fini2); } if (left_pts.size() > 0) { // The left line is formed here cv::fitLine(left_pts, left_line, CV_DIST_L2, 0, 0.01, 0.01); left_m = left_line[1] / left_line[0]; left_b = cv::Point(left_line[2], left_line[3]); } } // One the slope and offset points have been obtained, apply the line equation to obtain the line points int ini_y = inputImage.rows; int fin_y = 470; double right_ini_x = ((ini_y - right_b.y) / right_m) + right_b.x; double right_fin_x = ((fin_y - right_b.y) / right_m) + right_b.x; double left_ini_x = ((ini_y - left_b.y) / left_m) + left_b.x; double left_fin_x = ((fin_y - left_b.y) / left_m) + left_b.x; output[0] = cv::Point(right_ini_x, ini_y); output[1] = cv::Point(right_fin_x, fin_y); output[2] = cv::Point(left_ini_x, ini_y); output[3] = cv::Point(left_fin_x, fin_y); return output;}// TURN PREDICTIONstd::string LaneDetector::predictTurn() { std::string output; double vanish_x; double thr_vp = 10; // The vanishing point is the point where both lane boundary lines intersect vanish_x = static_cast<double>(((right_m*right_b.x) - (left_m*left_b.x) - right_b.y + left_b.y) / (right_m - left_m)); // The vanishing points location determines where is the road turning if (vanish_x < (img_center - thr_vp)) output = "Left Turn"; else if (vanish_x >(img_center + thr_vp)) output = "Right Turn"; else if (vanish_x >= (img_center - thr_vp) && vanish_x <= (img_center + thr_vp)) output = "Straight"; return output;}// PLOT RESULTSint LaneDetector::plotLane(cv::Mat inputImage, std::vector<cv::Point> lane, std::string turn) { std::vector<cv::Point> poly_points; cv::Mat output; // Create the transparent polygon for a better visualization of the lane inputImage.copyTo(output); poly_points.push_back(lane[2]); poly_points.push_back(lane[0]); poly_points.push_back(lane[1]); poly_points.push_back(lane[3]); cv::fillConvexPoly(output, poly_points, cv::Scalar(0, 0, 255), CV_AA, 0); cv::addWeighted(output, 0.3, inputImage, 1.0 - 0.3, 0, inputImage); // Plot both lines of the lane boundary cv::line(inputImage, lane[0], lane[1], cv::Scalar(0, 255, 255), 5, CV_AA); cv::line(inputImage, lane[2], lane[3], cv::Scalar(0, 255, 255), 5, CV_AA); // Plot the turn message cv::putText(inputImage, turn, cv::Point(50, 90), cv::FONT_HERSHEY_COMPLEX_SMALL, 3, cvScalar(0, 255, 0), 1, CV_AA); // Show the final output image cv::namedWindow("Lane", CV_WINDOW_AUTOSIZE); cv::imshow("Lane", inputImage); return 0;}
main函数
#include <iostream>#include <string>#include <vector>#include <opencv2/opencv.hpp>#include "LaneDetector.h"//#include "LaneDetector.cpp"int main() { // The input argument is the location of the video cv::VideoCapture cap("challenge_video.mp4"); if (!cap.isOpened()) return -1; LaneDetector lanedetector; // Create the class object cv::Mat frame; cv::Mat img_denoise; cv::Mat img_edges; cv::Mat img_mask; cv::Mat img_lines; std::vector<cv::Vec4i> lines; std::vector<std::vector<cv::Vec4i> > left_right_lines; std::vector<cv::Point> lane; std::string turn; int flag_plot = -1; int i = 0; // Main algorithm starts. Iterate through every frame of the video while (i < 540) { // Capture frame if (!cap.read(frame)) break; // Denoise the image using a Gaussian filter img_denoise = lanedetector.deNoise(frame); // Detect edges in the image img_edges = lanedetector.edgeDetector(img_denoise); // Mask the image so that we only get the ROI img_mask = lanedetector.mask(img_edges); // Obtain Hough lines in the cropped image lines = lanedetector.houghLines(img_mask); if (!lines.empty()) { // Separate lines into left and right lines left_right_lines = lanedetector.lineSeparation(lines, img_edges); // Apply regression to obtain only one line for each side of the lane lane = lanedetector.regression(left_right_lines, frame); // Predict the turn by determining the vanishing point of the the lines turn = lanedetector.predictTurn(); // Plot lane detection flag_plot = lanedetector.plotLane(frame, lane, turn); i += 1; cv::waitKey(25); } else { flag_plot = -1; } } return flag_plot;}
上述内容就是怎么在C++中使用opencv实现一个车道线识别功能,你们学到知识或技能了吗?如果还想学到更多技能或者丰富自己的知识储备,欢迎关注编程网行业资讯频道。