I have been trying to make an OpenCV-Py program to draw rectangle, line, and circle on mouse click and drag. I could successfully do it for line and rectangle but the code for the circle is wrong and I need help with that.
import numpy as np
import cv2 as cv
import math
drawing = False # true if mouse is pressed
ix,iy = -1,-1
# mouse callback function
def draw_circle(event,x,y,flags,param):
global ix,iy,drawing
if event == cv.EVENT_LBUTTONDOWN:
drawing = True
ix,iy = x,y
elif event == cv.EVENT_MOUSEMOVE:
if drawing == True:
k = cv.waitKey(33)
if k == ord('r'):
cv.rectangle(img,(ix,iy),(x,y),(0,255,0),-1)
elif k==ord('c'):
cv.circle(img,int(((ix+x)/2,(iy+y)/2)),int(math.sqrt( ((ix-x)**2)+((iy-y)**2) )),(0,0,255),-1)
elif k== ord('l'):
cv.line(img,(ix,iy),(x,y),(255,0,0),5)
elif event == cv.EVENT_LBUTTONUP:
drawing = False
img = np.zeros((512,512,3), np.uint8)
cv.namedWindow('image')
cv.setMouseCallback('image',draw_circle)
while(1):
cv.imshow('image',img)
k = cv.waitKey(1) & 0xFF
if k == 27:
break
cv.destroyAllWindows()
ERROR: Traceback (most recent call last):
File "mouse.py", line 19, in draw_circle
cv.circle(img,int(((ix+x)/2,(iy+y)/2)),int(math.sqrt( ((ix-x)**2)+((iy-y)**2) )),(0,0,255),-1)
TypeError: int() argument must be a string, a bytes-like object or a number, not 'tuple'
To dynamically draw a circle with OpenCV,
import numpy as np
import cv2
import math
drawing = False # true if mouse is pressed
ix,iy = -1,-1
# Create a function based on a CV2 Event (Left button click)
def draw_circle(event,x,y,flags,param):
global ix,iy,drawing
if event == cv2.EVENT_LBUTTONDOWN:
drawing = True
# we take note of where that mouse was located
ix,iy = x,y
elif event == cv2.EVENT_MOUSEMOVE:
drawing == True
elif event == cv2.EVENT_LBUTTONUP:
radius = int(math.sqrt( ((ix-x)**2)+((iy-y)**2)))
cv2.circle(img,(ix,iy),radius,(0,0,255), thickness=1)
drawing = False
# Create a black image
img = np.zeros((512,512,3), np.uint8)
# This names the window so we can reference it
cv2.namedWindow('image')
# Connects the mouse button to our callback function
cv2.setMouseCallback('image',draw_circle)
while(1):
cv2.imshow('image',img)
# EXPLANATION FOR THIS LINE OF CODE:
# https://stackoverflow.com/questions/35372700/whats-0xff-for-in-cv2-waitkey1/39201163
k = cv2.waitKey(1) & 0xFF
if k == 27:
break
# Once script is done, its usually good practice to call this line
# It closes all windows (just in case you have multiple windows called)
cv2.destroyAllWindows()
This is how to draw the circle dynamically.
As you drag the mouse you can see the size of the circle dynamically change.
import math
import numpy as np
import cv2 as cv
cv.namedWindow('image', cv.WND_PROP_ASPECT_RATIO)
drawing = False # true if mouse is pressed
# Coordinate
x1, y1, x2, y2 = -1, -1, -1, -1
def run():
img = cv.imread(f'image/python.png')
cv.imshow('image', img)
# Create a layer to draw circle. The layer has the same dimension of image
layer = np.zeros((img.shape[0], img.shape[1], 3), dtype="uint8")
# mouse callback function
def draw_circle(event, x, y, flags, param):
global x1, y1, x2, y2, drawing
# Manage different button state
if event == cv.EVENT_LBUTTONDOWN:
drawing = True
x1, y1 = x, y
elif event == cv.EVENT_MOUSEMOVE:
if drawing == True:
# Fill all value to 0 to clean layer
layer.fill(0)
cv.circle(layer, (x1, y1),calc_radius(x1, y1, x, y), (255, 0, 0), 1)
# Create a mask of shape
img2gray = cv.cvtColor(layer, cv.COLOR_BGR2GRAY)
ret, mask = cv.threshold(img2gray, 0, 255, cv.THRESH_BINARY)
# Create a copy of original image
_img = img.copy()
# Set the value of mask to 0, to avoid color overlap problems
_img[np.where(mask)] = 0
cv.imshow('image', np.where(layer == 0, _img, layer))
elif event == cv.EVENT_LBUTTONUP:
drawing = False
layer.fill(0)
cv.circle(layer, (x1, y1), calc_radius(x1, y1, x, y), (255, 0, 0), 1)
# Create a mask of shape
img2gray = cv.cvtColor(layer, cv.COLOR_BGR2GRAY)
ret, mask = cv.threshold(img2gray, 0, 255, cv.THRESH_BINARY)
_img = img.copy()
# Set the value of mask to 0, to avoid color overlap problems
_img[np.where(mask)] = 0
# Merge two array using Numpy where function
cv.imshow('image', np.where(layer == 0, _img, layer))
# Assig callback
cv.setMouseCallback('image', draw_circle)
# Service function to calculate radius (Pythagorean theorem)
def calc_radius(x1, y1, x2, y2):
delta_x = abs(x2 - x1)
delta_y = abs(y2 - y1)
return int(math.sqrt((delta_x**2)+(delta_y**2)))
while True:
k = cv.waitKey(1)
if k == ord("c"): # c to terminate a program
cv.destroyAllWindows()
break
if __name__ == '__main__':
run()
Related
I was trying to draw rectangles using mouse over an image, using openCV package in python. When ever I drew a rectangle, I got multiple rectangles overlapping one another, instead of a single rectangle. Like the below image
Here is my code. Please tell me where I went wrong and what needs to be corrected, so that I get only 1 rectangle.
import cv2
import numpy as np
drawing = False
ix,iy = -1, -1
img = cv2.imread('drawing_over_image/dog.jpg')
def draw(event, x, y, flags, params):
global ix, iy, drawing
if event == cv2.EVENT_LBUTTONDOWN:
ix,iy = x,y
drawing = True
elif event == cv2.EVENT_MOUSEMOVE:
if drawing:
if ix < x and iy < y:
cv2.rectangle(img=img, pt1=(ix,iy), pt2=(x,y), color=[255,0,0], thickness=1)
elif event == cv2.EVENT_LBUTTONUP:
drawing = False
cv2.rectangle(img=img, pt1=(ix,iy), pt2=(x,y), color=[255,0,0], thickness=1)
if __name__ == "__main__":
while True:
cv2.imshow(winname='image', mat=img)
cv2.setMouseCallback('image', draw)
if cv2.waitKey(1) == 27:
cv2.destroyAllWindows()
break
Its drawing multiple rectangles because you are drawing rectangles on every mouse move after user is pressing on the button. Instead you should draw whenever the event is done namely when the user release the left button. I fixed your code and add a basic ref image for you to see your rectangle when you are drawing. Hope it helps!
import cv2
import numpy as np
drawing = False
ix,iy = -1, -1
img = cv2.imread('drawing_over_image/dog.jpg')
refimg = img.copy()
def draw(event, x, y, flags, params):
global ix, iy, drawing
if event == cv2.EVENT_LBUTTONDOWN:
ix,iy = x,y
drawing = True
elif event == cv2.EVENT_MOUSEMOVE:
if drawing:
if ix < x and iy < y:
cv2.rectangle(img=refimg, pt1=(ix,iy), pt2=(x,y), color=[255,0,0], thickness=1)
elif event == cv2.EVENT_LBUTTONUP:
drawing = False
if ix < x and iy < y:
cv2.rectangle(img=img, pt1=(ix,iy), pt2=(x,y), color=[255,0,0], thickness=1)
if __name__ == "__main__":
while True:
cv2.imshow(winname='image', mat=refimg)
cv2.setMouseCallback('image', draw)
refimg = img.copy()
if cv2.waitKey(1) & 0xFF == 27:
cv2.destroyAllWindows()
break
Output:
You have two options:
use cv.selectROI()
do it all by hand
with selectROI()
rect = cv.selectROI("image", img, False)
cv.destroyWindow("image")
print(rect) # (x,y,w,h) or (0,0,0,0)
by hand
keep the original image
while dragging, from the event handler, draw on a copy, imshow() that
handle mousedown, mousemove, mouseup
call separate completion and cancelation functions to make it neat
sel1 = None # first point
sel2 = None # second point
def on_mouse(event, x, y, flags, params):
global sel1, sel2
if event == cv2.EVENT_LBUTTONDOWN:
sel1 = (x,y)
sel2 = None
elif event == cv2.EVENT_RBUTTONDOWN:
cancel_selection()
elif event == cv2.EVENT_MOUSEMOVE:
if sel1 is not None:
canvas = img.copy()
cv2.rectangle(canvas, sel1, (x,y), color=[255,0,0], thickness=2)
cv2.imshow("image", canvas)
elif event == cv2.EVENT_LBUTTONUP:
if sel1 is not None:
sel2 = (x,y)
selection_done(sel1, sel2)
sel1 = sel2 = None
def cancel_selection():
global sel1
sel1 = None
# restore display
cv2.imshow("image", img)
def selection_done(pt1, pt2):
# draw on the source image?
cv2.rectangle(img, pt1, pt2, color=[255,255,0], thickness=2)
cv2.imshow("image", img)
if __name__ == "__main__":
cv2.imshow('image', img)
cv2.setMouseCallback('image', on_mouse)
while True:
key = cv2.waitKey()
if key == 27: # ESC cancels ongoing selection, or exits the window
if sel1 is None:
break
else:
cancel_selection()
cv2.destroyAllWindows()
I try to run my bikes detection on opencv python. its work properly when I run it but when the program ends it comes to an error. I don't know how to fix it. Can somebody tell me where the problem is?
import cv2
import numpy as np
import pygame
import datetime as dt
from pygame import mixer
import time
#=============== Variable Mouse ==================#
drawing = False
point1 = ()
point2 = ()
drawingTwo = False
pointTwo_1 = ()
pointTwo_2 = ()
Mouse_count = False
#================================================#
def mouse_drawing(event, x, y, flags, params):
global point1, point2, drawing
global pointTwo_1, pointTwo_2, drawingTwo, Mouse_count
#----------Mouse 1-------
if Mouse_count == False:
if event == cv2.EVENT_LBUTTONDOWN:
if drawing is False:
drawing = True
point1 = (x, y)
#else:
#drawing = False
elif event == cv2.EVENT_MOUSEMOVE:
if drawing is True:
point2 = (x, y)
elif event == cv2.EVENT_LBUTTONUP:
drawing = False
Mouse_count = True
#================================================#
lastTime = dt.datetime.now()
currentTime = dt.datetime.now()
#Make Sound
pygame.mixer.init()
#create VideoCapture object and read from video file
cap = cv2.VideoCapture('test13.mp4')
cv2.namedWindow("Detecion motor")
cv2.setMouseCallback("Detecion motor", mouse_drawing)
while True:
ret, frame = cap.read()
car_cascade = cv2.CascadeClassifier('cascade11.xml')
#============================== ROI One ============================#
if point1 and point2:
#Rectangle marker
r = cv2.rectangle(frame, point1, point2, (100, 50, 200), 5)
frame_ROI = frame[point1[1]:point2[1],point1[0]:point2[0]]
#------------------Detect car ROI-------------------#
if drawing is False:
#convert video into gray scale of each frames
ROI_grayscale = cv2.cvtColor(frame_ROI, cv2.COLOR_BGR2GRAY)
#detect cars in the video
cars_ROI = car_cascade.detectMultiScale(ROI_grayscale, 1.1, 3)
if len(cars_ROI) > 0:
if (currentTime - lastTime).seconds > 20:
lastTime = dt.datetime.now()
sound = mixer.Sound('sirine2.wav')
sound.play()
for (x, y, w, h) in cars_ROI:
cv2.rectangle(frame_ROI, (x, y), (x + w, y + h), (0, 255, 0), 2)
currentTime = dt.datetime.now()
# cv2.putText(frame_ROI, "Jumlah Motor : " + str(cars_ROI.shape[0]), (10,frame_ROI.shape[0] -25), cv2.FONT_HERSHEY_TRIPLEX, 0.5,(0,255,0), 1)
# -------------------------------------------------#
#============================== ROI Two ============================#
#==================================================================#
cv2.imshow("Detecion motor", frame)
if cv2.waitKey(25) & 0xFF == ord('q'):
break
cap.release()
cv2.destroyAllWindows()
And then the error was:
Traceback (most recent call last):
File "D:/Skripsi/CarDetection-master/DeteksiMotor.py", line 65, in <module>
frame_ROI = frame[point1[1]:point2[1],point1[0]:point2[0]]
TypeError: 'NoneType' object is not subscriptable
Refining #mkrieger1's comment, we can see in your code that you are checking that both point1 and point2 for casting to Boolean True, so the problem is not there. By default, then this means that frame is equal to None. Replace your line:
if point1 and point2:
with:
if frame and point1 and point2:
I mean, frame is the result of what looks like an input operation, and these can fail in oh so many creative ways.
So during my webcam/video I would like to when I click my mouse, a rectangle is drawn at the mouse position, and the rectangle size is fixed ex. 80 X 80. In my current code, the rectangle follows the mouse but the size is always different. I want a fixed size exactly at the mouse position when I Click on a frame in the video.
Here is my code.
import os
import numpy as np
import cv2
from PIL import Image
import re
print('kaishi')
flag=0
drawing = False
point1 = ()
point2 = ()
ref_point = []
xvalues=[];
yvalues=[];
ref_point = []
cx=0;
cy=0;
def mouse_drawing(event, x, y, flags, params):
global point1, point2,
drawing,ref_point2,flag,refPt,cropping,cx,cy
if event == cv2.EVENT_LBUTTONDOWN:
drawing = True
point1 = (x, y)
xvalues.append(x)
yvalues.append(y)
cx =x;
cy=y;
elif event == cv2.EVENT_MOUSEMOVE:
if drawing is True:
point2 = (x, y)
elif event == cv2.EVENT_LBUTTONUP:
flag+=1;
print('finished square')
cap = cv2.VideoCapture(0)
cv2.namedWindow("Frame")
cv2.setMouseCallback("Frame", mouse_drawing)
while True:
_, frame = cap.read()
if point1 and point2 :
cv2.rectangle(frame,(200,cy),(cx,128),(0,0,255),0)
print(cx,cy)
flag=0;
cv2.imshow("Frame", frame)
key = cv2.waitKey(25)
if key== 13:
print('done')
elif key == 27:
break
cap.release()
cv2.destroyAllWindows()
The problem is that you fixed a point of the rectangle and the other point follows the mouse. In your code it would be here:
cv2.rectangle(frame,(200,cy),(cx,128),(0,0,255),0)
Now, it will depend how the rectangle will be, is the point where you click the top left point? if it is then it should be something like:
cv2.rectangle(frame,(cx,cy),(cx + 80, cy +80),(0,0,255),0)
This example will be for a 80 x 80 rectangle.... In your code this will happen when you click.
However, your code has a lot of unused code... I would do something like this:
import numpy as np
import cv2
drawing = False
point = (0,0)
def mouse_drawing(event, x, y, flags, params):
global point, drawing
if event == cv2.EVENT_LBUTTONDOWN:
drawing = True
point= (x, y)
cap = cv2.VideoCapture(0)
cv2.namedWindow("Frame")
cv2.setMouseCallback("Frame", mouse_drawing)
while True:
_, frame = cap.read()
if drawing :
cv2.rectangle(frame,point,(point[0]+80, point[1]+80),(0,0,255),0)
cv2.imshow("Frame", frame)
key = cv2.waitKey(25)
if key== 13:
print('done')
elif key == 27:
break
cap.release()
cv2.destroyAllWindows()
If you want the rectangle to follow your mouse after you clicked and to stop following after you release the button, change in the code I supplied before the mouse_drawing function like this:
def mouse_drawing(event, x, y, flags, params):
global point, drawing
if event == cv2.EVENT_LBUTTONDOWN:
drawing = True
point = (x, y)
elif event == cv2.EVENT_MOUSEMOVE:
if drawing is True:
point = (x, y)
elif event == cv2.EVENT_LBUTTONUP:
drawing = False
point = (x, y)
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()
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:
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:
This is a simple code and you can do a lot of things with mouse events.
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.
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)
I wrote the following script with OpenCV
import cv2
import numpy as np
cap = cv2.VideoCapture(0)
ix, iy = -1, -1
def draw_circle(event, x, y, flags, param):
global ix
global iy
ix,iy = x,y
if event == cv2.EVENT_LBUTTONDOWN:
cv2.circle(img, (200, 399), 10, (0, 255, 255), -1)
print("Text Put")
elif event == cv2.EVENT_LBUTTONUP:
print("EVENT_LBUTTONUP")
elif event == cv2.EVENT_RBUTTONDOWN:
print("Appuyé Droite")
elif event == cv2.EVENT_RBUTTONUP:
print("EVENT_RBUTTONUP")
elif event == cv2.EVENT_MOUSEMOVE:
print("Move on", x, y)
while(1):
ret, img = cap.read()
cv2.setMouseCallback('image', draw_circle)
cv2.imshow('image', img)
if cv2.waitKey(20) & 0xFF == 27:
break
cv2.destroyAllWindows()
But when I do a right button click, no circle appears on the display. What is wrong with my code ?
This code snippet may help you get started:
import cv2
import numpy as np
cap = cv2.VideoCapture(0)
ix, iy = -1,-1
def mouseCallback(event,x,y,flags,param):
global ix
global iy
if event == cv2.EVENT_LBUTTONDOWN:
ix = x # saves the position of the last click
iy = y
elif event == cv2.EVENT_LBUTTONUP:
print("EVENT_LBUTTONUP")
elif event == cv2.EVENT_RBUTTONDOWN:
print("Appuyé Droite")
elif event == cv2.EVENT_RBUTTONUP:
print("EVENT_RBUTTONUP")
def draw_circle_onscreen(frame, x,y):
cv2.circle(frame, (x,y), 10,(0, 0, 255),-1)
cv2.namedWindow('frame')
cv2.setMouseCallback('frame',mouseCallback) # mouse callback has to be set only once
while(1):
ret, img = cap.read()
draw_circle_onscreen(img,ix,iy) # draws circle on screen
cv2.imshow('frame',img)
if cv2.waitKey(20) & 0xFF == 27:
break
cv2.destroyAllWindows()