Reputation: 23
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()
Upvotes: 2
Views: 15562
Reputation: 383
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)
Upvotes: 1
Reputation: 21233
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:
Upvotes: 12
Reputation: 1006
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()
This is a simple code and you can do a lot of things with mouse events.
Upvotes: 2
Reputation: 411
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.
Upvotes: 1