'drawing rectangle in openCV python
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??
Solution 1:[1]
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
Solution 2:[2]
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()
Solution 3:[3]
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()
Solution 4:[4]
You will need 2 image variable to draw:
- draw_img show the image with complete rectangle.
- tmp_img is the draw_img with current draft for new rectangle.
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
Sources
This article follows the attribution requirements of Stack Overflow and is licensed under CC BY-SA 3.0.
Source: Stack Overflow
| Solution | Source |
|---|---|
| Solution 1 | Lynx |
| Solution 2 | LauriTK |
| Solution 3 | Ahsin Shabbir |
| Solution 4 | Vy Phuc |

