Reputation: 754
I am trying to draw rectangle using mouse as input with Opencv in python. I made this code from opencv documentation. There is problem while drawing rectangle that is when you try to drag from the start point rectangle is drawn all the way to end point. Like I showed in Images.
How can I draw clean unfilled rectangle? where I can see drawing an actual rectangle. Like we do in Paint
import cv2
import numpy as np
drawing = False
ix,iy = -1,-1
def draw_rect(event,x,y,flags,param):
global ix,iy,drawing,mode
if event == cv2.EVENT_LBUTTONDOWN:
drawing = True
ix,iy = x,y
elif event == cv2.EVENT_MOUSEMOVE:
if drawing == True:
cv2.rectangle(img,(ix,iy),(x,y),(0,255,0),1)
elif event == cv2.EVENT_LBUTTONUP:
drawing = False
cv2.rectangle(img,(ix,iy),(x,y),(0,255,0),1)
img = np.zeros((512,512,3), np.uint8)
cv2.namedWindow('image')
cv2.setMouseCallback('image',draw_rect)
while(1):
cv2.imshow('image',img)
k = cv2.waitKey(1) & 0xFF
if k == 27:
break
cv2.destroyAllWindows()
Can anyone tell me why is that happening? Any solution on it??
Upvotes: 4
Views: 22168
Reputation: 1
You will need 2 image variable to draw:
Here is my code:
def catch_point(event, x, y, flags, param):
global draw, cur_x, cur_y, tmp_img, draw_img
if event == cv.EVENT_LBUTTONDOWN:
draw = True
cur_x = x
cur_y = y
elif event == cv.EVENT_MOUSEMOVE:
if draw == True:
tmp_img = draw_img.copy()
cv.rectangle(tmp_img, (cur_x, cur_y), (x, y), (36,255,12), 2)
cv.imshow("draw_img", tmp_img)
elif event == cv.EVENT_LBUTTONUP:
draw_img = cv.rectangle(draw_img, (cur_x, cur_y), (x, y), (36,255,12), 2)
draw = False
draw_img = img.copy()
tmp_img = img.copy()
cv.namedWindow("draw_img", cv.WINDOW_NORMAL)
cv.setMouseCallback("draw_img", catch_point)
while True:
if draw == False:
cv.imshow("draw_img", draw_img)
key = cv.waitKey(1) & 0xFF
if key == ord("s"):
cv.destroyAllWindows()
break
Upvotes: 0
Reputation: 305
Building on previous answers, I managed to create a script that can draw rectangles and any size circles on an image. The rectangle updates as you move the mouse around while holding down the left mouse button. The circle gets bigger or smaller by pressing r or t, respectively. Press "m" to toggle between modes. Press "x" to reset the image (remove everything you drew). Here is the code, obviously it can be optimized more and I will be working on that.
import cv2
import numpy as np
drawing = False # true if mouse is pressed
mode = True # if True, draw rectangle. Press 'm' to toggle to curve
ix,iy = -1,-1
x_, y_ = 0,0
r = 15 #circle radius
# mouse callback function
def draw_shape(event,x,y,flags,param):
print(event)
global ix,iy,drawing,mode,x_,y_, r
if event == cv2.EVENT_LBUTTONDOWN:
print('inside mouse lbutton event....')
drawing = True
ix,iy = x,y
x_,y_ = x,y
elif event == cv2.EVENT_MOUSEMOVE and drawing:
copy = img.copy()
x_,y_ = x,y
if mode:
cv2.rectangle(copy,(ix,iy),(x_,y_),(0,255,0),1)
cv2.imshow("image", copy)
else:
cv2.circle(copy,(x,y),r,(0,0,255),1)
cv2.imshow('image', copy)
#
elif event == cv2.EVENT_LBUTTONUP:
print('inside mouse button up event')
drawing = False
if mode:
cv2.rectangle(img,(ix,iy),(x,y),(0,255,0),1)
else:
cv2.circle(img,(x,y),r,(0,0,255),1)
img = np.zeros((512,512,3), np.uint8)
temp_img = np.copy(img)
cv2.namedWindow('image')
cv2.setMouseCallback('image',draw_shape)
while(1):
# print('inside while loop...')
cv2.imshow('image',img)
if not cv2.EVENT_MOUSEMOVE:
copy = img.copy()
# print('x_: , y_ : '.format(x_,y_))
print(x_)
if mode == True:
cv2.rectangle(copy,(ix,iy),(x_,y_),(0,255,0),1)
cv2.imshow('image',copy)
else:
cv2.circle(copy,(x_,y_),r,(0,0,255),1)
cv2.imshow('image',copy)
k = cv2.waitKey(1) & 0xFF
if k == ord('m'): #toggle between circle and rectangle
mode = not mode
x_,y_ = -10,-10
ix,iy = -10,-10
elif k == ord('r') and not mode: #make circle bigger
r += 1
elif k == ord('t') and not mode: #make circle smaller
if r <=2:
r = 1
else:
r -= 1
elif k == ord('x'): #resets the image (removes circles and rectangles)
img = np.copy(temp_img)
x_,y_ = -10,-10
ix,iy = -10,-10
elif k == 27:
break
cv2.destroyAllWindows()
Upvotes: 0
Reputation: 1
I managed to solve this with using copies. This works now with just one imshow() update. Using global variables may not be the best.
import cv2 as cv
drawing = False
ix, iy = -1, -1
def draw_markers(event, x, y, flags, param):
global ix, iy, drawing, frame, frame_copy
if flags == cv.EVENT_FLAG_ALTKEY + cv.EVENT_FLAG_LBUTTON:
if event == cv.EVENT_LBUTTONDOWN:
print("Alt + lmouse down")
drawing = True
ix, iy = x, y
frame_copy = frame.copy()
elif event == cv.EVENT_MOUSEMOVE:
if drawing:
frame = cv.rectangle(
frame_copy.copy(), (ix, iy), (x, y), (0, 255, 0), 2)
elif event == cv.EVENT_LBUTTONUP:
print("Alt + lmouse up")
drawing = False
cv.rectangle(frame, (ix, iy), (x, y), (0, 255, 0), 2)
elif event == cv.EVENT_LBUTTONUP:
print("Draw crosshair")
cv.drawMarker(frame, (x, y), (255, 0, 0), 0, 16, 2, 8)
cap = cv.VideoCapture('video.avi')
cap.set(cv.CAP_PROP_POS_FRAMES, 1)
ret, frame = cap.read()
frame_copy = frame.copy()
cv.namedWindow('frame')
cv.setMouseCallback('frame', draw_markers)
while(True):
cv.imshow('frame', frame)
if cv.waitKey(1) & 0xFF == ord('q'):
break
cap.release()
cv.destroyAllWindows()
Upvotes: 0
Reputation: 291
you could change your press mouse event to something like this
elif event == cv2.EVENT_MOUSEMOVE:
if drawing==True:
copy = image.copy()
cv2.rectangle(copy,(ix,iy),(x,y),(0,255,0),1)
cv2.imshow("image", copy)
script will create copies of image with rectangles based on current x's and y's and display real time effect
Upvotes: 0
Reputation: 17239
When you move the mouse during event == cv2.EVENT_MOUSEMOVE
you also drawing the recatngle at the same time cv2.rectangle(img,(ix,iy),(x,y),(0,255,0),1)
.
Try following piece of code.
import cv2
import numpy as np
drawing = False # true if mouse is pressed
mode = True # if True, draw rectangle.
ix,iy = -1,-1
# mouse callback function
def draw_circle(event,x,y,flags,param):
global ix,iy,drawing,mode
if event == cv2.EVENT_LBUTTONDOWN:
drawing = True
ix,iy = x,y
elif event == cv2.EVENT_MOUSEMOVE:
if drawing == True:
if mode == True:
cv2.rectangle(img,(ix,iy),(x,y),(0,255,0),3)
a=x
b=y
if a != x | b != y:
cv2.rectangle(img,(ix,iy),(x,y),(0,0,0),-1)
else:
cv2.circle(img,(x,y),5,(0,0,255),-1)
elif event == cv2.EVENT_LBUTTONUP:
drawing = False
if mode == True:
cv2.rectangle(img,(ix,iy),(x,y),(0,255,0),2)
else:
cv2.circle(img,(x,y),5,(0,0,255),-1)
img = np.zeros((512,512,3), np.uint8)
cv2.namedWindow('image')
cv2.setMouseCallback('image',draw_circle)
while(1):
cv2.imshow('image',img)
k = cv2.waitKey(1) & 0xFF
if k == ord('m'):
mode = not mode
elif k == 27:
break
cv2.destroyAllWindows()
Hope it'll solve your problem. Cheers.
Expected Output:
Above piece of code will work only black background image. But we can draw rectangle to any images so -
Try following piece of code.
# import the necessary packages
import cv2
import argparse
# now let's initialize the list of reference point
ref_point = []
def shape_selection(event, x, y, flags, param):
# grab references to the global variables
global ref_point, crop
# if the left mouse button was clicked, record the starting
# (x, y) coordinates and indicate that cropping is being performed
if event == cv2.EVENT_LBUTTONDOWN:
ref_point = [(x, y)]
# check to see if the left mouse button was released
elif event == cv2.EVENT_LBUTTONUP:
# record the ending (x, y) coordinates and indicate that
# the cropping operation is finished
ref_point.append((x, y))
# draw a rectangle around the region of interest
cv2.rectangle(image, ref_point[0], ref_point[1], (0, 255, 0), 2)
cv2.imshow("image", image)
# construct the argument parser and parse the arguments
ap = argparse.ArgumentParser()
ap.add_argument("-i", "--image", required=True, help="Path to the image")
args = vars(ap.parse_args())
# load the image, clone it, and setup the mouse callback function
image = cv2.imread(args["image"])
clone = image.copy()
cv2.namedWindow("image")
cv2.setMouseCallback("image", shape_selection)
# keep looping until the 'q' key is pressed
while True:
# display the image and wait for a keypress
cv2.imshow("image", image)
key = cv2.waitKey(1) & 0xFF
# press 'r' to reset the window
if key == ord("r"):
image = clone.copy()
# if the 'c' key is pressed, break from the loop
elif key == ord("c"):
break
# close all open windows
cv2.destroyAllWindows()
Save the file as capture_events.py
and for testing we selected a demo picture which located at the same directory. Now run the code by following -
python capture_events.py --image demo.jpg
Expected Output:
If some reason we want to re select any portion of the image , we can simply press 'r' to get rid of the bad selection to try a new one.
Hope , it'll will help more. Check this gist, you can implement more functionality on this. Cheers.
Upvotes: 9