Search code examples
pythonopencvlinedrawcontour

How to draw a line from two points and then let the line complete drawing until reaching a contour point with opencv, python?


I am using opencv and python for programming and I am trying to draw a line between two points that I know their coordinates, and then let the line complete until it reaches the end of the contour as shown in the image bellow. The contour in my case is actually of an image face, but I have provided a circle here for explanation. So what I am trying to achieve is to get the edge of the head at that point intersecting with the line and contour. Is there a way to draw a line from two points and then let the line complete drawing until reaching the contour? enter image description here


Solution

  • I can think of one easy method off the top of my head that doesn't involve incrementally updating the image: on one blank image, draw a long line extending from point one in the direction of point two, and then AND the resulting image with the an image of the single contour drawn (filled). This will stop the line at the end of the contour. Then you can either use that mask to draw the line, or get the minimum/maximum x, y coords if you want the coordinates of the line.

    To walk through an example, first we'll find the contour and draw it on a blank image:

    contours = cv2.findContours(img, cv2.RETR_EXTERNAL, cv2.CHAIN_APPROX_NONE)[1]
    contour_img = np.zeros_like(img)
    cv2.fillPoly(contour_img, contours, 255)
    

    Then, if we have the points p1 and p2, find the direction they're heading in and find a point far off in that distance and draw that line on a new blank image (here I used a distance of 1000 pixels from p1):

    p1 = (250, 250)
    p2 = (235, 210)
    
    theta = np.arctan2(p1[1]-p2[1], p1[0]-p2[0])
    endpt_x = int(p1[0] - 1000*np.cos(theta))
    endpt_y = int(p1[1] - 1000*np.sin(theta))
    
    line_img = np.zeros_like(img)
    cv2.line(line_img, (p1[0], p1[1]), (endpt_x, endpt_y), 255, 2)
    

    Then simply cv2.bitwise_and() the two images together

    contour_line_img = cv2.bitwise_and(line_img, contour_img)
    

    Here is an image showing the points, the line extending past the contour, and the line breaking off at the contour.

    Line breaking at contour

    Edit: Note that this will only work if your contours are convex. If there is any concavity and the line goes through that concave part, it will continue to draw on the other side of it. For e.g. in Silencer's answer, if both points were inside one of the ear and pointed towards the other ear, you'd want the contour to stop once it hit an edge, but mine will continue to draw on the other ear. I think an iterative method like Silencer's is the best for the general case, but I like the simplicity of this method if you know you have convex contours or if your points will be in a place to not have this issue.

    Edit2: Someone else on Stack answered their own question about the Line Iterator class in Python by creating one: openCV 3.0 python LineIterator