【C++&&OpenCv】道路交通标线之白色虚线和鱼骨线检测

道路交通标线之白色虚线和鱼骨线检测

流程图

代码思路完全按照流程图实现,其中的参数可以根据实际要处理的环境调整
检测白色虚线流程图

检测白色虚线流程图

检测鱼骨线流程图

检测鱼骨线流程图

代码

#include <iostream>
#include "opencv2/opencv.hpp"
#include "opencv2/highgui.hpp"
#include "math.h"
using namespace std;
using namespace cv;


double getLineLength(Point p1,Point p2){
    return sqrt(pow(p1.x-p2.x,2)+pow(p1.y-p2.y,2));
}
double getLineGradient(Point p1,Point p2){
    if(p1.x==p2.x||p1.x-p2.x==0){
        p1.x = p1.x + 1;
    }
    return (p1.y-p2.y)/(p1.x-p2.x);
}
//处理离群直线
void handlingOutlierLines(vector<Vec4i> &Lines,size_t threshold){
    double maxLength,minLength;
    maxLength = getLineLength(Point(Lines[0][0],Lines[0][1]),Point(Lines[0][2], Lines[0][3]));
    minLength = getLineLength(Point(Lines[0][0],Lines[0][1]),Point(Lines[0][2], Lines[0][3]));
    cout<<"enter handlingOutlierLines"<<endl;
    int number = Lines.size();
    while(number>threshold){
        double mean = 0.0;
        double sum = 0.0;
        for(size_t i=0;i<number;i++){
            if(getLineLength(Point(Lines[i][0],Lines[i][1]),Point(Lines[i][2], Lines[i][3]))>maxLength){
                maxLength = getLineLength(Point(Lines[i][0],Lines[i][1]),Point(Lines[i][2], Lines[i][3]));
            }
            if(getLineLength(Point(Lines[i][0],Lines[i][1]),Point(Lines[i][2], Lines[i][3]))<minLength){
                minLength = getLineLength(Point(Lines[i][0],Lines[i][1]),Point(Lines[i][2], Lines[i][3]));
            }
            sum += getLineGradient(Point(Lines[i][0],Lines[i][1]),Point(Lines[i][2], Lines[i][3]));
        }
        mean = sum/number;
        cout<<"mean: "<<mean<<endl;
        vector<Vec4i>::iterator it = Lines.begin();
        while(it!=Lines.end()){
            Vec4i vec = *it;
            double gradient = getLineGradient(Point(vec[0],vec[1]),Point(vec[2], vec[3]));
            if(fabs(gradient-mean)>fabs(20*mean)){
               it = Lines.erase(it);
            }else{
                ++it;
            }
        }
        number = Lines.size();
    }
    cout<<"maxLength: "<<maxLength<<" minLength: "<<minLength<<"lineNumber: "<<Lines.size()<<endl;
};
//检测空洞
Mat connected_components_stat(Mat& image) {

    // 二值化
    Mat gray, binary;
    cvtColor(image, gray, COLOR_BGR2GRAY);
    threshold(gray, binary, 200, 255, THRESH_BINARY);

    //开运算、闭运算
    Mat kernel = getStructuringElement(MORPH_RECT, Size(3, 3), Point(-1, -1));
    //morphologyEx(binary, binary, MORPH_DILATE, kernel);
    //morphologyEx(binary, binary, MORPH_CLOSE, kernel);

    imshow("binaryImage",binary);

    //计算连通域
    Mat labels = Mat::zeros(image.size(), CV_32S);
    Mat stats, centroids;
    int num_labels = connectedComponentsWithStats(binary, labels, stats, centroids, 8, 4);
    //使用不同的颜色标记连通域
    vector<unsigned char> colors(num_labels);
    // 背景颜色
    colors[0] = 0;
    //将白色空洞染成黑色
    for (int i = 1; i < num_labels; i++) {
        Vec2d pt = centroids.at<Vec2d>(i, 0);
        int x = stats.at<int>(i, CC_STAT_LEFT);
        int y = stats.at<int>(i, CC_STAT_TOP);
        int width = stats.at<int>(i, CC_STAT_WIDTH);
        int height = stats.at<int>(i, CC_STAT_HEIGHT);
        int area = stats.at<int>(i, CC_STAT_AREA);
        if(area<=20||area>400||(y<image.rows/2&&area>100)){
            colors[i] = 0;
        }else{
            colors[i] = 255;
        }
    }

    Mat result = Mat::zeros(image.size(),CV_8U);
    int w = image.cols;
    int h = image.rows;
    for (int row = 0; row < h; row++) {
        for (int col = 0; col < w; col++) {
            int label = labels.at<int>(row, col);
            if (label == 0) continue;
            result.at<unsigned char>(row, col) = colors[label];
        }
    }

    imshow("result", result);
    return result;
}

void detectWhiteDottedLine(string imgPath){
    Mat image = imread(imgPath);
    imshow("image",image);

    Mat result;
    result = connected_components_stat(image);

//    Mat gaussImage;
//    GaussianBlur(binaryImage,gaussImage,Size(3,3),0,0);
//    imshow("gaussImage",gaussImage);
//
    Mat cannyImage;
    Canny(result,cannyImage,235,250);
    imshow("cannyImage",cannyImage);

    Mat kernel = getStructuringElement(MORPH_RECT, Size(3, 3), Point(-1, -1));
    //morphologyEx(cannyImage,cannyImage,MORPH_DILATE,kernel);

    Mat OutputImage = imread(imgPath);
    vector<Vec4i> Lines;
    HoughLinesP(cannyImage, Lines, 1, CV_PI / 360,30, 20, 20);
    cout<<"检测到的直线的数量是:"<<Lines.size()<<endl;
    //handlingOutlierLines(Lines,Lines.size()-1);
    double max;
    Vec4i max1,max2;
    max = getLineLength(Point(Lines[0][0],Lines[0][1]),Point(Lines[0][2], Lines[0][3]));
    max1 = {Lines[0][0],Lines[0][1],Lines[0][2],Lines[0][3]};
    for (size_t i = 0; i < Lines.size(); i++)
    {
        double lineLength = getLineLength(Point(Lines[i][0],Lines[i][1]),Point(Lines[i][2], Lines[i][3]));
        if(lineLength>max){
            max = lineLength;
            max2 = max1;
            max1 ={Lines[i][0],Lines[i][1],Lines[i][2], Lines[i][3]};
        }
        if(lineLength>0){
            line(OutputImage, Point(Lines[i][0], Lines[i][1]), Point(Lines[i][2], Lines[i][3]), Scalar(0, 0, 255), 2, 8);
        }
    }
    cout<<max1<<" "<<max2<<" "<<max<<endl;
    imshow("OutputImage", OutputImage);
    waitKey(0);
    destroyAllWindows();
}
void detectWhiteDottedLine2(string imgPath){
    Mat image = imread(imgPath,IMREAD_GRAYSCALE);
    imshow("image",image);
    Mat binaryImage;
    threshold(image,binaryImage,220,250,THRESH_BINARY);
    imshow("binaryImage",binaryImage);

    Mat morphImage;
    Mat kernel = getStructuringElement(MORPH_RECT, Size(3, 3), Point(-1, -1));
    morphologyEx(binaryImage,morphImage,MORPH_DILATE,kernel);
    imshow("morphImage",morphImage);
//
//    Mat gaussImage;
//    GaussianBlur(binaryImage,gaussImage,Size(3,3),0,0);
//    imshow("gaussImage",gaussImage);

    Mat cannyImage;
    Canny(morphImage,cannyImage,235,250);
    imshow("cannyImage",cannyImage);

    //计算连通域
    Mat labels = Mat::zeros(image.size(), CV_32S);
    Mat stats, centroids;
    int num_labels = connectedComponentsWithStats(cannyImage, labels, stats, centroids, 8, 4);
    //使用不同的颜色标记连通域
    vector<unsigned char> colors(num_labels);
    // 背景颜色
    colors[0] = 0;
    //将白色空洞染成黑色
    for (int i = 1; i < num_labels; i++) {
        Vec2d pt = centroids.at<Vec2d>(i, 0);
        //int x = stats.at<int>(i, CC_STAT_LEFT);
        int y = stats.at<int>(i, CC_STAT_TOP);
        //int width = stats.at<int>(i, CC_STAT_WIDTH);
        //int height = stats.at<int>(i, CC_STAT_HEIGHT);
        int area = stats.at<int>(i, CC_STAT_AREA);
        if(area<=35){
            colors[i] = 0;
        }else{
            colors[i] = 255;
        }
    }

    Mat result = Mat::zeros(image.size(),CV_8U);
    int w = image.cols;
    int h = image.rows;
    for (int row = 0; row < h; row++) {
        for (int col = 0; col < w; col++) {
            int label = labels.at<int>(row, col);
            if (label == 0) continue;
            result.at<unsigned char>(row, col) = colors[label];
        }
    }

    imshow("result", result);
//    Mat gaussImage;
//    GaussianBlur(result,gaussImage,Size(3,3),0,0);
//    imshow("gaussImage",gaussImage);

    Mat OutputImage = imread(imgPath);
    vector<Vec4i> Lines;
    HoughLinesP(result, Lines, 1, CV_PI / 360,50, 20, 30);
    cout<<"检测到的直线的数量是:"<<Lines.size()<<endl;
    //handlingOutlierLines(Lines,Lines.size()-1);
    double max;
    Vec4i max1,max2;
    max = getLineLength(Point(Lines[0][0],Lines[0][1]),Point(Lines[0][2], Lines[0][3]));
    max1 = {Lines[0][0],Lines[0][1],Lines[0][2],Lines[0][3]};
    for (size_t i = 0; i < Lines.size(); i++)
    {
        double lineLength = getLineLength(Point(Lines[i][0],Lines[i][1]),Point(Lines[i][2], Lines[i][3]));
        if(lineLength>max){
            max = lineLength;
            max2 = max1;
            max1 ={Lines[i][0],Lines[i][1],Lines[i][2], Lines[i][3]};
        }
        if(lineLength>10&&lineLength<90){
            line(OutputImage, Point(Lines[i][0], Lines[i][1]), Point(Lines[i][2], Lines[i][3]), Scalar(0, 0, 255), 2, 8);
        }
    }
    cout<<max1<<" "<<max2<<" "<<max<<endl;
    imshow("OutputImage", OutputImage);
    waitKey(0);
    destroyAllWindows();
}

double getAngle(Point angle,Point edge1,Point edge2){
    double vec1_x = edge1.x - angle.x;
    double vec1_y = edge1.y - angle.y;
    double vec2_x = edge2.x - angle.x;
    double vec2_y = edge2.y - angle.y;
    double cos = (vec1_x*vec2_x+vec1_y*vec2_y)/( sqrt(pow(vec1_x,2)+pow(vec1_y,2)) * sqrt(pow(vec2_x,2)+pow(vec2_y,2)) );
    double result = acos(cos)/CV_PI*180;
    return result;
}
void detectFishboneLine(string imgPath){
    Mat image = imread(imgPath);
    Mat grayImage;
    cvtColor(image,grayImage,COLOR_RGB2GRAY);
    Mat binaryImage;
    threshold(grayImage,binaryImage,210,255,THRESH_BINARY);
    imshow("binaryImage",binaryImage);

    Mat dst0 = Mat::zeros(binaryImage.size(), CV_8UC3);
    vector<vector<Point>> contours;
    vector<Vec4i> hierarchy;
    findContours(binaryImage, contours, hierarchy, RETR_LIST, CHAIN_APPROX_SIMPLE);
    Mat contoursImage = image.clone();
    drawContours(contoursImage,contours,-1,Scalar(0,0,0),3);
    imshow("contoursImage",contoursImage);
    vector<vector<Point>> points(contours.size());
    for(int i = 0; i < contours.size(); i++){
        approxPolyDP(contours[i],points[i],3, true);
    }

    for(int i = 0; i < contours.size(); i++){
        if(points[i].size()==4){
            Point p1 = points[i][0];
            Point p2 = points[i][1];
            Point p3 = points[i][2];
            Point p4 = points[i][3];
            double sum = 0;
            double a1 = getAngle(p1,p2,p4);
            double a2 = getAngle(p2,p1,p3);
            double a3 = getAngle(p3,p2,p4);
            double a4 = getAngle(p4,p3,p1);
            sum = a1 + a2 + a3 + a4;
            if(sum>300&&sum<400){
                //cout<<"###"<<i<<points[i]<<endl;
                drawContours(image,points,i,Scalar(0,255,255),3);
            }
        }

    }
    imshow("output", image);

    waitKey(0);
    destroyAllWindows();
}
int main() {

    string imgPath = ".\imageProcess\\xx3.png";
    string imgPath1 = ".\imageProcess\\ygx1.png";
    int choice;
    cout<<"Detect white dotted line input 1, detect fishbone line input 2:"<<endl;
    cin>>choice;
    if(choice==1){

        cout<<"Enter the picture path + picture name:"<<endl;
        cin>>imgPath;
        detectWhiteDottedLine2(imgPath);
    }else{
        cout<<"Enter the picture path + picture name:"<<endl;
        cin>>imgPath1;
        detectFishboneLine(imgPath1);
    };

    return 0;
}

效果图

因为是同一套代码使用不同的测试图,所以检测效果不是特别好。如果是固定的路段或者是可以截取到比较固定的ROI的话,那么针对性地进行调参,结果会更好一些。
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

参考文章

python+opencv车道线,实线虚线的检测
入门版的车道线检测(python+opencv)
OpenCV实战案例——车道线识别
HoughLinesP参数设置(看完还不会你砍死我!!!)
连通域标记图像ConnectedComponents
OpenCV C++(九)----几何形状的检测和拟合
approxPolyDP(拟合多边形)

  • 0
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
好的,以下是一个简单的 C++OpenCV 的代码,用于检测道路交通禁止直行标志: ```c++ #include <iostream> #include <opencv2/opencv.hpp> using namespace std; using namespace cv; int main() { Mat inputImage = imread("input.jpg"); // 读取输入图像 if (inputImage.empty()) { cerr << "Failed to load image!" << endl; return -1; } Mat hsvImage; cvtColor(inputImage, hsvImage, COLOR_BGR2HSV); // 转换颜色空间为 HSV // 设置红色范围的上下限 Scalar lowerRed = Scalar(0, 100, 100); Scalar upperRed = Scalar(10, 255, 255); Scalar lowerRed2 = Scalar(160, 100, 100); Scalar upperRed2 = Scalar(179, 255, 255); // 提取红色区域 Mat mask1, mask2, mask; inRange(hsvImage, lowerRed, upperRed, mask1); inRange(hsvImage, lowerRed2, upperRed2, mask2); bitwise_or(mask1, mask2, mask); // 通过形态学操作来去噪 Mat kernel = getStructuringElement(MORPH_RECT, Size(3, 3)); morphologyEx(mask, mask, MORPH_OPEN, kernel); // 寻找轮廓 vector<vector<Point>> contours; findContours(mask, contours, RETR_EXTERNAL, CHAIN_APPROX_SIMPLE); // 绘制矩形框 for (size_t i = 0; i < contours.size(); i++) { Rect rect = boundingRect(contours[i]); double aspectRatio = rect.width / (double)rect.height; if (aspectRatio > 0.8 && aspectRatio < 1.2) { // 判断长宽比是否符合要求 rectangle(inputImage, rect, Scalar(0, 255, 0), 2); } } // 显示结果 imshow("Result", inputImage); waitKey(0); return 0; } ``` 在这个代码中,我们首先读取输入图像,然后将其转换为 HSV 颜色空间。接下来,我们定义一个红色的范围,然后使用 inRange 函数提取红色区域。为了去除噪声,我们使用形态学操作进行开运算。最后,我们查找轮廓并绘制符合要求的矩形框。 请注意,这个代码仅仅是一个简单的示例,可能无法适应所有情况。如果您需要更加准确和鲁棒的结果,请根据实际情况进行修改和优化。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值