4

I do have a binary image as shown below after applying various preprocessing and detection pipelines onto original image.

Intersecting Runways

As seen in the picture there are actually 2 runways (tarmacs) for planes which are crossing each other on an intersection region. What I need is to split both runways and return their contours. I've checked opencv functions regarding contour features but had no luck. cv2.fitLine seems ok but it only works if there is only a single line in the contour. Resulting image when the masks are applied should be looking like this: enter image description here

Jeru Luke
  • 20,118
  • 13
  • 80
  • 87
colt.exe
  • 708
  • 8
  • 24
  • In the intersection point, the lines are not visible so if you wanna do that you should go on with an assumption that we should consider the contour points will continue like the previous contours. You can cut each line by cutting from intersection. For this you can create different algorithms. – Yunus Temurlenk Aug 16 '20 at 10:11
  • Check out [cv2.HoughLines](http://opencv-python-tutroals.readthedocs.io/en/latest/py_tutorials/py_imgproc/py_houghlines/py_houghlines.html). It won't give you the exact contours, but you will get the directions of the runways, which you can use to get the contours. – karposhark Aug 17 '20 at 07:47

2 Answers2

3

Here's a possible approach, just done in Terminal with ImageMagick, but you should be able to do pretty much the same in Python with Wand or with scikit-image and the medial_axis.

First, skeletonise the image:

magick runways.png -threshold 50% -morphology Thinning:-1 Skeleton skeleton.png

enter image description here

Then run a "Hough Line Detection" looking for lines longer than 130 pixels and ask for the results in a tabular form:

magick skeleton.png -hough-lines 9x9+130 mvg:-

Output

# Hough line transform: 9x9+130
viewbox 0 0 464 589
# x1,y1 x2,y2 # count angle distance
line 297.15,0 286.869,589  # 255 1 476
line 0,591.173 464,333.973  # 189 61 563

That means it has detected 2 lines:

  • a line from coordinates 297,0 to coordinates 286,589, with a length=255 pixels at 1 degree to the vertical
  • a line from coordinates 0,591 to coordinates 464,333, with a length=189 pixels at 61 degrees to the vertical

Just to illustrate, I'll draw the first in red and the second in green:

magick runways.png                       \
   -fill red  -draw "line 297,0 286,589" \
   -fill lime -draw "line 0,591 464,333" result.png

enter image description here

Keywords: Python, image processing, skeleton, skeletonise, thinning, runway, runways, intersection, Hough Line Detection.

Mark Setchell
  • 191,897
  • 31
  • 273
  • 432
  • Thanks for the nice approach, but what I need is contour info for each line segment so I can mask them. – colt.exe Aug 30 '20 at 17:08
2

I tried to solve your problem with C++ referencing my old answer.

some steps:

--after finding contours find defect points by convexityDefects

approxPolyDP(contours[i], contours[i], 9, true);
convexHull(contours[i], contoursHull, true);
convexityDefects(contours[i], contoursHull, defects);

enter image description here

create two copy of binary image and draw lines using defect points

Vec4i defpoint0 = defects[0];
Vec4i defpoint1 = defects[1];
Vec4i defpoint2 = defects[2];
Vec4i defpoint3 = defects[3];
line(bw0, contours[i][defpoint0[2]], contours[i][defpoint1[2]], Scalar(0), 2);
line(bw0, contours[i][defpoint2[2]], contours[i][defpoint3[2]], Scalar(0), 2);

line(bw1, contours[i][defpoint0[2]], contours[i][defpoint3[2]], Scalar(0), 2);
line(bw1, contours[i][defpoint1[2]], contours[i][defpoint2[2]], Scalar(0), 2);

enter image description here

enter image description here

find contours from images and draw them ( i hardcoded found contour index, need to be improved )

findContours(bw0, contours, RETR_EXTERNAL, CHAIN_APPROX_NONE);
drawContours(src, contours, 1, Scalar((rand() & 255), (rand() & 255), (rand() & 255)), 2);

findContours(bw1, contours, RETR_EXTERNAL, CHAIN_APPROX_NONE);
drawContours(src, contours, 2, Scalar((rand() & 255), (rand() & 255), (rand() & 255)), 2);

enter image description here

#include <opencv2/highgui.hpp>
#include <opencv2/imgproc.hpp>
#include <iostream>

using namespace cv;
using namespace std;

int main(int argc, char** argv)
{
    Mat src = imread("e:/test/crossing_lines.png");
    if (src.empty())
        return -1;

    Mat bw,bw0,bw1;
    cvtColor(src, bw, COLOR_BGR2GRAY);
    bw = bw > 127;
    bw0 = bw.clone();
    bw1 = bw.clone();
    // Find contours
    vector<vector<Point> > contours;
    vector<int> contoursHull;
    vector<Vec4i> defects;
    findContours(bw, contours, RETR_EXTERNAL, CHAIN_APPROX_NONE);

    for (size_t i = 0; i < contours.size(); i++)
    {
        if (contourArea(contours[i]) > 500)
        {
            approxPolyDP(contours[i], contours[i], 9, true);
            convexHull(contours[i], contoursHull, true);
            convexityDefects(contours[i], contoursHull, defects);
 
            Vec4i defpoint0 = defects[0];
            Vec4i defpoint1 = defects[1];
            Vec4i defpoint2 = defects[2];
            Vec4i defpoint3 = defects[3];
            line(bw0, contours[i][defpoint0[2]], contours[i][defpoint1[2]], Scalar(0), 2);
            line(bw0, contours[i][defpoint2[2]], contours[i][defpoint3[2]], Scalar(0), 2);

            line(bw1, contours[i][defpoint0[2]], contours[i][defpoint3[2]], Scalar(0), 2);
            line(bw1, contours[i][defpoint1[2]], contours[i][defpoint2[2]], Scalar(0), 2);
        }
    }
    findContours(bw0, contours, RETR_EXTERNAL, CHAIN_APPROX_NONE);
    drawContours(src, contours, 1, Scalar((rand() & 255), (rand() & 255), (rand() & 255)), 2);

    findContours(bw1, contours, RETR_EXTERNAL, CHAIN_APPROX_NONE);
    drawContours(src, contours, 2, Scalar((rand() & 255), (rand() & 255), (rand() & 255)), 2);
    imshow("src", src);
    imshow("bw0", bw0);
    imshow("bw1", bw1);
    waitKey();
    return 0;
}
Dharman
  • 30,962
  • 25
  • 85
  • 135
sturkmen
  • 3,495
  • 4
  • 27
  • 63
  • 1
    So It comes down to defect point detection and splitting into multiple pieces and rest is easy. But this has to be generalized in some way since defects points are hand picked. There are actually c(4,2) = 6 lines that could be drawn. You have used 4 in examples and the rest 2 are forming a cross which could be discarded this way. Besides there are 3 lines formed after a split so which one to choose is still ambigious. I think fusing @Mark Setchell 's approach to determine the direction and using yours to split, I could come upwith a result. Really greateful for your efforts. – colt.exe Aug 30 '20 at 21:01
  • 1
    i think convexityDefects returns points in clockwise order so pairing 0-1, 2-3 and 1-2, 0-3 never produce crossing lines. – sturkmen Aug 30 '20 at 21:08
  • as i mention in the answer you need to find right contour index which i hardcoded. – sturkmen Aug 30 '20 at 21:13