Extract single line contours from Canny edges

Muffo picture Muffo · Aug 6, 2013 · Viewed 13k times · Source

I'd like to extract the contours of an image, expressed as a sequence of point coordinates.

With Canny I'm able to produce a binary image that contains only the edges of the image. Then, I'm trying to use findContours to extract the contours. The results are not OK, though.

For each edge I often got 2 lines, like if it was considered as a very thin area. I would like to simplify my contours so I can draw them as single lines. Or maybe extract them with a different function that directly produce the correct result would be even better.

I had a look on the documentation of OpenCV but I was't able to find anything useful, but I guess that I'm not the first one with a similar problem. Is there any function or method I could use?

Here is the Python code I've written so far:

def main():
    img = cv2.imread("lena-mono.png", 0)

    if img is None:
        raise Exception("Error while loading the image")

    canny_img = cv2.Canny(img, 80, 150)

    contours, hierarchy = cv2.findContours(canny_img, cv2.RETR_EXTERNAL, cv2.CHAIN_APPROX_NONE)
    contours_img = cv2.cvtColor(img, cv2.COLOR_GRAY2BGR)

    scale = 10
    contours_img = cv2.resize(contours_img, (0, 0), fx=scale, fy=scale)

    for cnt in contours:
        color = np.random.randint(0, 255, (3)).tolist()
        cv2.drawContours(contours_img,[cnt*scale], 0, color, 1)

    cv2.imwrite("canny.png", canny_img)
    cv2.imwrite("contours.png", contours_img)

The scale factor is used to highlight the double lines of the contours. Here are the links to the images:

  • Lena greyscale
  • Edges extracted with Canny
  • Contours: 10x zoom where you can see the wrong results produced by findContours

Any suggestion will be greatly appreciated.

Answer

timlukins picture timlukins · Aug 6, 2014

If I understand you right, your question has nothing to do with finding lines in a parametric (Hough transform) sense.

Rather, it is an issue with the findContours method returning multiple contours for a single line.

This is because Canny is an edge detector - that means it is filter attuned to the image intensity gradient which occurs on both sides of a line.

So your question is more akin to: “how can I convert low-level edge features to single line?”, or perhaps: “how can I navigate the contours hierarchy to detect single lines?"

This is a fairly common topic - and here is a previous post which proposed one solution:

OpenCV converting Canny edges to contours