2

I am trying to write a function which will open an image and draw a circle where the left mouse button is clicked. the circle's size can then be adjusted using the mousewheel/keyboard. Also, every click will print a label in sequence e.g. 1st circle puts label '1', 2nd circle drawn puts a label'2' and so on. I have managed to get the circle and the label on the image but i am unsure how to increase the radius or change the label with different clicks.

import cv2
import numpy as np

# Create a black image and a window
windowName = 'Drawing'
img = cv2.imread('000025.png',cv2.IMREAD_COLOR)
cv2.namedWindow(windowName)








# mouse callback function
def draw_circle(event, x, y, flags, param):
    if event == cv2.EVENT_LBUTTONDOWN:
        cv2.circle(img, (x,y), 30, (255, 0,), 1)
        font = cv2.FONT_HERSHEY_SIMPLEX
        cv2.putText(img,'label' , (x + 30, y + 30), font, 1, (200, 255, 155), 1, cv2.LINE_AA)




# bind the callback function to window
cv2.setMouseCallback(windowName, draw_circle)


def main():
    while (True):
        cv2.imshow(windowName, img)
        if cv2.waitKey(20) == 27:
            break

    cv2.destroyAllWindows()


if __name__ == "__main__":
    main()

4 Answers 4

12

Using the following code you can visualize the circle while moving the mouse as well. I have supplemented the code provided by Salman by adding another condition involving MOUSEMOVE event.

import cv2
import numpy as np
import math

drawing = False

def draw_circle(event, x, y, flags, param):
    global x1, y1, drawing, radius, num, img, img2
    if event == cv2.EVENT_LBUTTONDOWN:
        drawing = True
        x1, y1 = x, y
        radius = int(math.hypot(x - x1, y - y1))
        cv2.circle(img, (x1,y1), radius, (255, 0, 0), 1)

    elif event == cv2.EVENT_MOUSEMOVE:
        if drawing == True:
            a, b = x, y
            if a != x & b != y:
                img = img2.copy()
                radius = int(math.hypot(a - x1, b - y1))
                cv2.circle(img, (x1,y1), radius, (255, 0, 0), 1)

    elif event == cv2.EVENT_LBUTTONUP:
        drawing = False
        num += 1
        radius = int(math.hypot(x - x1, y - y1))
        cv2.circle(img, (x1,y1), radius, (255, 0, 255), 1)
        font = cv2.FONT_HERSHEY_SIMPLEX
        cv2.putText(img, '_'.join(['label', str(num)]), (x + 20, y + 20), font, 1, (200, 255, 155), 1, cv2.LINE_AA)
        img2 = img.copy()


if __name__ == "__main__":
    num = 0
    windowName = 'Drawing'

    img = np.zeros((500, 500, 3), np.uint8)
    img2 = img.copy()
    cv2.namedWindow(windowName)
    cv2.setMouseCallback(windowName, draw_circle)
    while (True):
        cv2.imshow(windowName, img)
        if cv2.waitKey(20) == 27:
            break

    cv2.destroyAllWindows()

Sample output:

enter image description here

Sign up to request clarification or add additional context in comments.

Comments

2

I think this may works for you:

import cv2
import numpy as np
import math


# mouse callback function
def draw_circle(event, x, y, flags, param):
    global x1, y1, radius, num
    if event == cv2.EVENT_LBUTTONDOWN:
        x1, y1 = x, y

    if event == cv2.EVENT_LBUTTONUP:
        num += 1
        radius = int(math.hypot(x - x1, y - y1))
        cv2.circle(img, (x1,y1), radius, (255, 0,), 1)

        font = cv2.FONT_HERSHEY_SIMPLEX
        cv2.putText(img, f'label: {num}', (x + 30, y + 30), font, 1, (200, 255, 155), 1, cv2.LINE_AA)


if __name__ == "__main__":
    num = 0
    # Create a black image and a window
    windowName = 'Drawing'
    img = cv2.imread('img.jpg', cv2.IMREAD_COLOR)
    cv2.namedWindow(windowName)
    # bind the callback function to window
    cv2.setMouseCallback(windowName, draw_circle)
    while (True):
        cv2.imshow(windowName, img)
        if cv2.waitKey(20) == 27:
            break

    cv2.destroyAllWindows()

Result: enter image description here

This is a simple code and you can do a lot of things with mouse events.

Comments

1

First you have to keep all coordinates (or other attributes) of your drawables in global dynamic object.

You have to give guidance to the app, if you are drawing circle, label or other drawable. It can be done by creating menu items in the OpenCV window or by key presses (I have done both). You have to keep track of context (is next click x,y coords of center of cirle, point in the circle (for radius calc, unless you decide to use mousewheel/kbd for it) left-up corner of rectangle, etc.

You have to store the created drawables in the said global object.

If you want to edit/delete the existing drawable, you have to make iterator function, that detects closest drawable (by its mid- or other point) for proper selection.

All above is doable in OpenCV alone.

Comments

1

Python class implementation of getting mouse click points in an image using OpenCV mouse click callback. You can make an object of this class and use getpt(n, img) method to select n points in an image using mouse click. Edit and use for your purpose.

import cv2
import numpy as np
#events = [i for i in dir(cv2) if 'EVENT' in i]
#print (events)

class MousePts:
    def __init__(self,windowname,img):
        self.windowname = windowname
        self.img1 = img.copy()
        self.img = self.img1.copy()
        cv2.namedWindow(windowname,cv2.WINDOW_NORMAL)
        cv2.imshow(windowname,img)
        self.curr_pt = []
        self.point   = []

    def select_point(self,event,x,y,flags,param):
        if event == cv2.EVENT_LBUTTONDOWN:
            self.point.append([x,y])
            #print(self.point)
            cv2.circle(self.img,(x,y),5,(0,255,0),-1)
        elif event == cv2.EVENT_MOUSEMOVE:
            self.curr_pt = [x,y]
            #print(self.point)
                   
    def getpt(self,count=1,img=None):
        if img is not None:
            self.img = img
        else:
            self.img = self.img1.copy()
        cv2.namedWindow(self.windowname,cv2.WINDOW_NORMAL)
        cv2.imshow(self.windowname,self.img)
        cv2.setMouseCallback(self.windowname,self.select_point)
        self.point = []
        while(1):
            cv2.imshow(self.windowname,self.img)
            k = cv2.waitKey(20) & 0xFF
            if k == 27 or len(self.point)>=count:
                break
            #print(self.point)
        cv2.setMouseCallback(self.windowname, lambda *args : None)
        #cv2.destroyAllWindows()
        return self.point, self.img
         
if __name__=='__main__':
    img = np.zeros((512,512,3), np.uint8)
    windowname = 'image'
    coordinateStore = MousePts(windowname,img)

    pts,img = coordinateStore.getpt(3)
    print(pts)
        
    pts,img = coordinateStore.getpt(3,img)
    print(pts)
    
    cv2.imshow(windowname,img)
    cv2.waitKey(0)
    

1 Comment

While this code may solve the question, including an explanation of how and why this solves the problem would really help to improve the quality of your post, and probably result in more up-votes. Remember that you are answering the question for readers in the future, not just the person asking now. Please edit your answer to add explanations and give an indication of what limitations and assumptions apply.

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.