检测二进制掩码上的车道线

问题描述 投票:0回答:1

我有一条道路的二元蒙版,该蒙版有点不规则(有时甚至比图像中描绘的还要多)。

我已经尝试过在OpenCV中使用houghLine来检测边界线,但是边界线不符合预期。我尝试过腐蚀和膨胀来使事情变得顺利,但是没有运气。另外,由于路径是弯曲的,因此甚至难以使用houghLines检测边界线。如何修改代码以更好地检测行?

    img2=cv2.erode(img2,None,iterations=2)
    img2=cv2.dilate(img2,None,iterations=2)
    can=cv2.Canny(img2,150,50)
    lines=cv2.HoughLinesP(can,1,np.pi/180,50,maxLineGap=50,minLineLength=10)
    if(lines is not None):
        for x in lines:
            #print(lines[0])
            #mask=np.zeros(frame2.shape,dtype=np.uint8)
            #roi=lines
            #cv2.fillPoly(mask,roi,(255,255,255))
            #cv2.imshow(mask)
            for x1,y1,x2,y2 in x:
                cv2.line(frame2,(x1,y1),(x2,y2),(255,0,0),2)

enter image description here

python-3.x opencv
1个回答
0
投票

您说霍夫失败了,但您没有说为什么。为什么您的输出“不符合预期”?以我的经验,霍夫线检测的关键点有两个:1)传递给它的边缘遮罩和2)如何过滤结果线。您应该对这两个步骤进行微调,而Hough应该足以解决您的问题。

我不知道线检测器给您带来什么样的问题,但是假设您对其他检测车道的方法感兴趣(正如您的问题所建议的那样)。您至少可以尝试两种方法:1)鸟瞰道路的变化–由于您的所有线现在都是平行线,因此线检测变得容易得多。和2)轮廓检测(而不是线条)。

让我们研究2,以及可以获得什么样的结果。听着,伙计,我用C ++给出了答案,但我会做笔记。我尝试强调重要的想法,以便您可以用自己选择的语言来实现它们。但是,如果您只需要CTRL + C和CTRL + V解决方案,那是可以的,但是此答案对您没有帮助。

好吧,我们先读取图像并将其转换为二进制图像。我们的目标是首先获得边缘。相当标准的东西:

//Read input image:
std::string imagePath = "C://opencvImages//lanesMask.png";
cv::Mat testImage = cv::imread( imagePath );

//Convert BGR to Gray:
cv::Mat grayImage;
cv::cvtColor( testImage, grayImage, cv::COLOR_RGB2GRAY );

//Get binary image via Otsu:
cv::Mat binaryImage;
cv::threshold( grayImage, binaryImage, 0, 255, cv::THRESH_OTSU );

现在,只需将此图像传递给Canny的Edge检测器。参数也很标准。根据Canny的文档,下限阈值和上限阈值之间的比率是3:]

//Get Edges via Canny:
cv::Mat testEdges;

//Setup lower and upper thresholds for Canny’s edge detection:
float lowerThreshold = 30;
float upperThreshold = 3 * lowerThreshold;

cv::Canny( binaryImage, testEdges, lowerThreshold, upperThreshold );

您的口罩非常好;这些是Canny发现的优势:

enter image description here

现在,这是我们正在尝试不同的地方。我们将不使用霍夫的行检测,而是找到遮罩的轮廓

。每个轮廓由points组成。我们正在寻找的实际上是直线,可以<< fitting>到这些点的直线。实现这一目标的方法不止一种。我提出了一种聚类算法K-means想法是,您可以看到,这些点可以分为4组:车道的消失点(那里应该是2个端点)和道路的2个起点。如果我们给K-means轮廓的点,并告诉它将数据分为4个单独的组,我们应该得到这4个点的均值(位置)。

让我们尝试一下。第一步是在边缘蒙版中找到轮廓:

//Get contours: std::vector< std::vector<cv::Point> > contours; std::vector< cv::Vec4i > hierarchy; cv::findContours( testEdges, contours, hierarchy, CV_RETR_TREE, CV_CHAIN_APPROX_SIMPLE, cv::Point(0, 0) );

K均值在其输入上需要特定的数据类型。我将使用cv::Point2f向量存储所有轮廓点。让我们设置K-means使用的变量:

//Set up the data containers used by K-means: cv::Mat centers; cv::Mat labels; std::vector<cv::Point2f> points; //the data for clustering is stored here

接下来,让我们遍历轮廓并将每个点存储在Point2f向量内,因此我们可以将其进一步传递给k-means。让我们使用循环绘制轮廓,并确保我们没有弄乱东西:

//Loop thru the found contours: for( int i = 0; i < (int)contours.size(); i++ ){ //Set a color & draw contours: cv::Scalar color = cv::Scalar( 0, 256, 0 ); cv::drawContours( testImage, contours, i, color, 2, 8, hierarchy, 0, cv::Point() ); //This is the current vector of points that is being processed: std::vector<cv::Point> currentVecPoint = contours[i]; //Loop thru it and store each point as a float point inside a plain vector: for(int k = 0; k < (int)currentVecPoint.size(); k++){ cv::Point currentPoint = currentVecPoint[k]; //Push (store) the point into the vector: points.push_back( currentPoint ); } }

这些是找到的轮廓:

enter image description here

[现在,我的向量中有轮廓点。让我们将信息传递给K-means://设置K均值:int clusterCount = 4; //将集合除以的簇数整数尝试次数= 5; //使用不同的初始标签执行算法的次数

int flags = cv::KMEANS_PP_CENTERS; cv::TermCriteria criteria = cv::TermCriteria( CV_TERMCRIT_ITER | CV_TERMCRIT_EPS, 10, 0.01 ); //The call to kmeans: cv::kmeans( points, clusterCount, labels, criteria, attempts, flags, centers );

仅此而已。 K均值的结果在

centers

矩阵中。矩阵的每一行应有2列,表示一个点中心。在这种情况下,矩阵的大小为4 x2。让我们绘制该信息:enter image description here

正如预期的那样,有4个中心点,每个中心点是一个群集的平均值。现在,非常酷,这个近似值足以满足您的应用需求吗?只有你知道!您可以使用这些点并延伸两条线,但这可能会改善此结果。

© www.soinside.com 2019 - 2024. All rights reserved.