在OpenCV中检测轮廓之间的虚线

时间:2016-07-05 18:31:55

标签: c++ opencv computer-vision vision

今天很棘手,但绝对可以做到,我觉得我走在了正确的轨道上。我正试图检测图像中的虚线。我这样做是通过找到最合适的轮廓对,其斜率也与彼此创建的角度对齐。请参阅下面的示例代码块:

//Get canny mat
Mat frame, framegray, detectededges, dummyimg, cannyedges;
cvtColor(frame, framegray, CV_BGR2GRAY);
blur(framegray, detectededges, Size(3,3));
double otsu_thresh_val = cv::threshold(detectededges, dummyimg,0, 255, CV_THRESH_BINARY | CV_THRESH_OTSU);
cv::Canny( detectededges, cannyedges, otsu_thresh_val*0.5, otsu_thresh_val );

//
Scalar color(255,255,0,255);

//Find contours
vector<vector<Point>> contours;
vector<Vec4i> hierarchy;
findContours(cannyedges,contours,hierarchy,CV_RETR_CCOMP,CV_CHAIN_APPROX_SIMPLE);
double contourlength = arcLength(contours[idx], false);
vector<Vec4f> vectorofbestfits;

//Find dotted lines
for (vector<Point> contour : contours){
    //Some filtering
    if ((contourlength > 70.0) && (contours[idx].size() > 5)){
        Vec4f bestfitline;
        fitLine(contour, bestfitline,CV_DIST_L2,0,0.01,0.01);
        vectorofbestfits.push_back(bestfitline);
    }
}

float mtol = 0.05;
for (Vec4f veci : vectorofbestfits){
    float mi = veci[1]/veci[0];
    for (Vec4f vecj : vectorofbestfits){
        float mj = vecj[1]/vecj[0];
        double length = cv::norm(Mat(Point(veci[2],veci[3])),Mat(Point(vecj[2],vecj[3])));
        if (length < 30){
            continue;
        }
        float mk = (veci[3]-vecj[3])/(veci[2]-vecj[2]);
        mi = abs(mi);
        mj = abs(mj);
        mk = abs(mk);
        float mij = abs(mi - mj);
        float mjk = abs(mj - mk);
        float mki = abs(mk - mi);
        if ((mij < mtol) && (mjk < mtol) && (mki < mtol)){
            line(frame,Point(veci[2],veci[3]),Point(vecj[2],vecj[3]),color,2);
        }
    }
}

方法是: 1)为轮廓创建一组最佳拟合线 2)逐步通过数组得到线的斜率 3)再次逐行通过阵列和每隔一行的斜率 4)计算由两个直线中心产生的直线的斜率 5)将所有3个斜率相互比较,并使用公差值

进行过滤

下面的代码会从我的图片中生成大量的线条,但是没有一条线条出现明显的虚线。我在想斜坡计算有些不对劲。现在它是如此无效我需要创建一些图像和测试而不是使用真实世界的图形。什么跳出来?

此外,下面的代码计算量很大,应用程序用于视频解释和帧率是至关重要的,因此任何提高性能的建议都会受到赞赏。

寻找像这样的比赛: image

编辑:

这是一些更清晰的代码。我也意识到在获得差异之前获取斜率的绝对值是没有任何意义的,只是创建一堆更符合的偶然轮廓。我需要再玩一些,看看是否有帮助:

//Get canny mat
Mat frame, framegray, detectededges, dummyimg, cannyedges;
cvtColor(frame, framegray, CV_BGR2GRAY);
blur(framegray, detectededges, Size(3,3));
double otsu_thresh_val = cv::threshold(detectededges, dummyimg,0, 255,
    CV_THRESH_BINARY | CV_THRESH_OTSU);
cv::Canny( detectededges, cannyedges, otsu_thresh_val*0.5, otsu_thresh_val );

//Set color for Lines
Scalar color(255,255,0,255);

//Find contours
vector<vector<Point>> contours;
vector<Vec4i> hierarchy;
findContours(cannyedges,contours,hierarchy,CV_RETR_CCOMP,CV_CHAIN_APPROX_SIMPLE);
double contourlength = arcLength(contours[idx], false);
vector<Vec4f> arrayofbestfits;

//Create an array of best fit Lines
for (vector<Point> contour : contours){
    //Filter out contours that are too small
    if ((contourlength > 70.0) && (contours[idx].size() > 5)){
        Vec4f bestfitLine;
        fitLine(contour, bestfitLine,CV_DIST_L2,0,0.01,0.01);
        arrayofbestfits.push_back(bestfitLine);
    }
}

float SlopeTolerance = 0.05;
for (Vec4f firstIterationLine : arrayofbestfits){
    float firstIterationSlope = firstIterationLine[1]/firstIterationLine[0];
    for (Vec4f secondIterationLine : arrayofbestfits){
        //Filter out Lines too close in proxmity
        double length = cv::norm(Mat(Point(firstIterationLine[2],firstIterationLine[3])),
            Mat(Point(secondIterationLine[2],secondIterationLine[3])));
        if (length < 30){
            continue;
        }
        //Find slope between two points
        float commonSlope = (firstIterationLine[3]-secondIterationLine[3])
            /(firstIterationLine[2]-secondIterationLine[2]);
        //Find absolute value of differences (makes comparison simpler)
        float commonSlopediff = abs(firstIterationSlope - secondIterationSlope);
        float secondtocommonSlopediff = abs(secondIterationSlope - commonSlope);
        float commontofirstSlopediff = abs(commonSlope - firstIterationSlope);
        //If within tolerances draw the line bridging the two best fit lines together
        if ((commonSlopediff < SlopeTolerance) && (secondtocommonSlopediff < SlopeTolerance) && (commontofirstSlopediff < SlopeTolerance)){
            Line(frame,Point(firstIterationLine[2],firstIterationLine[3]),
                Point(secondIterationLine[2],secondIterationLine[3]),color,2);
        }
    }
}

1 个答案:

答案 0 :(得分:0)

根据您的示例图片,您似乎正在尝试检测具有以下约束的线段:

  1. 段具有相似的斜率(或方向)
  2. 段落在类似的线上。
  3. 此问题似乎非常适合RANSAC解决方案。

    使用简单的2D RANSAC,您可以找到大多数数据点对齐的行。

    1. 您可以通过随机抽样子集(在本例中为2的子集)并连接子集中的点来求解此行。
    2. 然后,设置窗口大小,并包括位于连接子集的行内的所有其他点+/-窗口大小。 这些点称为内点。
    3. 您希望找到最大化内部数量的线段。
    4. 有关详细信息,请参阅these slides on RANSAC

      在您的情况下,您希望稍微修改问题,以反映您正在寻找线段而不是点的事实。 有很多方法可以实现这一目标。

      一种解决方案是根据斜率对细分进行聚类,然后将简单的2D RANSAC应用于每个聚类,并将细分视为点。

      我希望这会有所帮助。