I am trying to draw a polygon between the coordinates which would be obtained by clicking by mouse events.
The first click should define the starting point of the polygon. Each additional click should sketch a line segment from the previous click. When whole clicked points sketch the polygon, inside of the polygon should be filled.
Can someone please suggest how to draw polygons between the points I click on the image? I am considering cv2.polylines function, however I have no idea how to integrate that with SetMouseCallback function.
To make the user interface more intuitive (since it would be very difficult for the user to click exactly in the same spot as the starting point), let's use the following actions:
We will need several variables to keep track of our progress:
(x, y)
We will use a mouse callback to update those variables periodically when the appropriate mouse events occur:
EVENT_MOUSEMOVE -- mouse has moved, update the current positionEVENT_LBUTTONDOWN -- user pressed the left mouse button, add current position to list of pointsEVENT_RBUTTONDOWN -- user pressed the right mouse button, mark data entry as completeFinally, we will have a function implementing the display loop.
This function will first create a named window, draw a blank canvas, and set up the mouse callback. Then it will repeatedly keep updating the screen by:
cv2.polyline
cv2.line.Once the data entry process is complete, the function will draw the final filled polygon and a clean canvas image, show it, and when the user presses a key return the final image.
import numpy as np
import cv2
# ============================================================================
CANVAS_SIZE = (600,800)
FINAL_LINE_COLOR = (255, 255, 255)
WORKING_LINE_COLOR = (127, 127, 127)
# ============================================================================
class PolygonDrawer(object):
    def __init__(self, window_name):
        self.window_name = window_name # Name for our window
        self.done = False # Flag signalling we're done
        self.current = (0, 0) # Current position, so we can draw the line-in-progress
        self.points = [] # List of points defining our polygon
    def on_mouse(self, event, x, y, buttons, user_param):
        # Mouse callback that gets called for every mouse event (i.e. moving, clicking, etc.)
        if self.done: # Nothing more to do
            return
        if event == cv2.EVENT_MOUSEMOVE:
            # We want to be able to draw the line-in-progress, so update current mouse position
            self.current = (x, y)
        elif event == cv2.EVENT_LBUTTONDOWN:
            # Left click means adding a point at current position to the list of points
            print("Adding point #%d with position(%d,%d)" % (len(self.points), x, y))
            self.points.append((x, y))
        elif event == cv2.EVENT_RBUTTONDOWN:
            # Right click means we're done
            print("Completing polygon with %d points." % len(self.points))
            self.done = True
    def run(self):
        # Let's create our working window and set a mouse callback to handle events
        cv2.namedWindow(self.window_name, flags=cv2.CV_WINDOW_AUTOSIZE)
        cv2.imshow(self.window_name, np.zeros(CANVAS_SIZE, np.uint8))
        cv2.waitKey(1)
        cv2.cv.SetMouseCallback(self.window_name, self.on_mouse)
        while(not self.done):
            # This is our drawing loop, we just continuously draw new images
            # and show them in the named window
            canvas = np.zeros(CANVAS_SIZE, np.uint8)
            if (len(self.points) > 0):
                # Draw all the current polygon segments
                cv2.polylines(canvas, np.array([self.points]), False, FINAL_LINE_COLOR, 1)
                # And  also show what the current segment would look like
                cv2.line(canvas, self.points[-1], self.current, WORKING_LINE_COLOR)
            # Update the window
            cv2.imshow(self.window_name, canvas)
            # And wait 50ms before next iteration (this will pump window messages meanwhile)
            if cv2.waitKey(50) == 27: # ESC hit
                self.done = True
        # User finised entering the polygon points, so let's make the final drawing
        canvas = np.zeros(CANVAS_SIZE, np.uint8)
        # of a filled polygon
        if (len(self.points) > 0):
            cv2.fillPoly(canvas, np.array([self.points]), FINAL_LINE_COLOR)
        # And show it
        cv2.imshow(self.window_name, canvas)
        # Waiting for the user to press any key
        cv2.waitKey()
        cv2.destroyWindow(self.window_name)
        return canvas
# ============================================================================
if __name__ == "__main__":
    pd = PolygonDrawer("Polygon")
    image = pd.run()
    cv2.imwrite("polygon.png", image)
    print("Polygon = %s" % pd.points)
Drawing in progress, we have 5 points entered, and the current segment is being shown as a darker line pointing to the current position of the mouse:

Drawing is complete, and the program is showing the whole polygon filled:


Adding point #0 with position(257,144)
Adding point #1 with position(657,263)
Adding point #2 with position(519,478)
Adding point #3 with position(283,383)
Adding point #4 with position(399,126)
Adding point #5 with position(142,286)
Adding point #6 with position(165,38)
Completing polygon with 7 points.
Polygon = [(257, 144), (657, 263), (519, 478), (283, 383), (399, 126), (142, 286), (165, 38)]
                        If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With