Firstly, please be nice as I'm only getting started with OpenCV in python and my python knowledge isn't spectacular.
I am trying to run OpenCV with numpy. I've made a white canvas where I keep track of the number of left-click events with count, so that I can
start drawing a circle with a mouseclick, or cv.EVENT_LBUTTONDOWN and count%2==1
let the radius grow at mouse movement with cv.EVENT_MOUSEMOVE and count%2==0 (and show the growing radius)
permanently "print" the circle on a second click with cv.EVENT_LBUTTONDOWN and count%2==0
Also, I have made two rectangles blue and green for color selection and I haven't allowed starting the circle within the bounds of these rectangles. Here, canvas is the continuously updated frame variable and frame is a matrix I use to refresh canvas when needed.
Now, in the output, when I drag my mouse, I expect the frames to refresh. So I let canvas=frame. In case of cv.EVENT_LBUTTONDOWN, I instead set frame=canvas so that it saves the final output I want to keep in the output frame.
However, as you can see in the outputs:
1.without clicking rectangles,
2.touching green and
3.touching both blue and green
it leaves a trace of the earlier frames(i.e it saves them to the frame). Not only that, I see the value of global variable frame changing automatically at cv.EVENT_MOUSEMOVE even though there isn't an assignment to frame in the callback function itself for that case! I see just one output with
frame and canvas aren't equivalent
in my termninal,just after the first click.
I am using OpenCV's setMouseCallback() routine for the callback.
Can anyone guide me on what I'm doing wrong here? I know using global variables like this is taboo but I was only guided by some lectures I found.
import cv2 as cv
import numpy as np
point=(0,0)
colour=(0,0,0)
line_width= 5
thickness=-1
count=1
radius=1
value=2**16-1
frame=value*np.ones((600,600,3),'uint16')
canvas=value*np.ones((600,600,3),'uint16')
def func(event,x,y,flags,param):
global point,radius,line_width,thickness,colour,count,frame,canvas
rect1=((x<=150 and x>=50) and (y>=50 and y<=150))
rect2=((x>=200 and x<=300) and (y>=50 and y<=150))
cv.rectangle(canvas,
(50,50),
(150,150),
color=(2**16-1,0,0),
thickness=-1)
cv.putText(canvas,
'Blue',
fontFace=cv.FONT_HERSHEY_SIMPLEX,
org=(70,70),
fontScale=0.5,
thickness=1,
color=(0,0,0))
cv.rectangle(canvas,
(200,50),
(300,150),
color=(0,2**16-1,0),
thickness=-1)
cv.putText(canvas,
'Green',
fontFace=cv.FONT_HERSHEY_SIMPLEX,
fontScale=0.5,
thickness=1,
org=(220,70),
color=(0,0,0))
if event==cv.EVENT_LBUTTONDOWN and rect1:
colour=(2**16-1,0,0)
elif event==cv.EVENT_LBUTTONDOWN and rect2:
colour=(0,2**16-1,0)
elif (event==cv.EVENT_LBUTTONDOWN) and count%2==1:
point=(x,y)
count=count+1
elif event==cv.EVENT_MOUSEMOVE and count%2==0:
if not np.array_equiv(canvas,frame):
print("frame and canvas aren't equivalent")
canvas=frame
radius=round(np.sqrt((x-point[0])**2+(y-point[1])**2))
cv.circle(canvas, point, radius,colour,line_width)
elif event==cv.EVENT_LBUTTONDOWN and count%2==0:
cv.circle(canvas, point, radius,colour,line_width)
count=count+1
frame=canvas
cv.namedWindow("Frame")
cv.setMouseCallback("Frame",func)
while True:
cv.imshow("Frame",canvas)
ch=cv.waitKey(1)
if ch & 0xFF == ord('q'):
break
cv.imwrite("Img.jpg",canvas)
cv.destroyAllWindows()
Related
Is there a way to clear the graph in PySimpleGUI before redraw a new image? i notice the function window["-GRAPH-"].draw_image() is causing a serious memory leak when the program run for sometime, as it is trying to stack the picture on top of all drawn images.
Background:
My app is trying to show a live feed from a webcam, meanwhile will also do some drawing (depending on mouse click) on top of the camera feed. In order to detect the mouse click event from the camera feed, im using sg.Graph to capture mouse position.
sample_app_display: user label the box of object in a live camera feed
Code Snippet:
sg.Graph(853, (0, 480), (853, 0), key="-GRAPH-", change_submits=True, drag_submits=False)
...
camera = my_opencv_library(device=0)
while True:
event, values = window.read(timeout=20)
if event == "-GRAPH-":
camera.update_coordinate(values["-GRAPH-"])
# obtain live feed with runtime drawing (based on mouse click)
frame = camera.get_frame()
imgbytes = cv2.imencode(".png", frame)[1].tobytes()
window["-GRAPH-"].draw_image(data=imgbytes, location=(0,0))
Erase the Graph - Removes all figures previously "drawn" using the Graph methods
Erase all figures on sg.Graph by
window['-GRAPH-'].erase()
Remove from the Graph the figure represented by id.
Erase specified figure on sg.Graph by ids
window['-GRAPH-'].delete_figure(ids)
The ids is given to you anytime you call a drawing primitive, like
ids = window["-GRAPH-"].draw_image(data=imgbytes, location=(0,0))
Update code
sg.Graph(853, (0, 480), (853, 0), key="-GRAPH-", change_submits=True, drag_submits=False)
...
camera = my_opencv_library(device=0)
ids = None
while True:
event, values = window.read(timeout=20)
if event == "-GRAPH-":
camera.update_coordinate(values["-GRAPH-"])
# obtain live feed with runtime drawing (based on mouse click)
frame = camera.get_frame()
imgbytes = cv2.imencode(".png", frame)[1].tobytes()
if ids is not None:
window["-GRAPH-"].delete_figure(ids)
ids = window["-GRAPH-"].draw_image(data=imgbytes, location=(0,0))
I am making a scene where there is a thumbs-up image that is supposed to get bigger on mouse hover, and shrink back to normal size when the mouse is no longer hovering.
This is how I make the thumbs-up image:
thumbs_up_image = pygame.image.load("./plz_like.png")
thumbs_up_rect = thumbs_up_image.get_rect(topleft=(screen.get_width() // 2 - thumbs_up_image.get_width() + 75,
screen.get_height() // 2 + thumbs_up_image.get_height() - 225))
And this is how I make it get bigger:
if thumbs_up_rect.collidepoint(pygame.mouse.get_pos()):
thumbs_up_image = pygame.transform.scale(thumbs_up_image,
[n + 50 for n in thumbs_up_image.get_size()])
thumbs_up_rect = thumbs_up_image.get_rect()
This is how the image is blited:
screen.blit(thumbs_up_image, thumbs_up_rect)
The problem is that when I hover on the thumbs-up image, it first goes to the top-left corner of the screen. Then, when I hover on it again, it gets super big and pixelated.
What am I doing wrong?
I managed to figure it out by myself.
This is how I do it:
First, I prepared a bigger version of the image and it's rect: (as shown below)
big_thumbs_image = pygame.transform.scale(thumbs_up_image, [i + 50 for i in thumbs_up_image.get_size()])
big_thumbs_image_rect = thumbs_up_image.get_rect(
topleft=(screen.get_width() // 2 - thumbs_up_image.get_width() + 55,
screen.get_height() // 2 + thumbs_up_image.get_height() - 250))
Then, when the small image's rect collides with the mouse, blit the bigger image:
if thumbs_up_rect.collidepoint(pygame.mouse.get_pos()):
screen.blit(big_thumbs_image, big_thumbs_image_rect)
You are not showing the code that actually renders the image to the screen.; But basically: you are not saving the original size - at each hover event it will grow and grow (and it will grow once per frame, if that code is run in the mainloop).
You need a variable to hold the original image, one to tell your code the image has already been resized, and an else clause on this if to restore the original image: pygame won't do that for you.
Also, when you use the get_rect for the image, its top-left position will always be "0, 0" - you have to translate this top-left corner to a suitable coordinate- getting the rectangle center of the original sprite (wherever the data of its location on the screen is kept), and setting the same center on the new rect should work.
And finally, prefer "rotozoom" than "scale" - Pygame documentation is clear that the second method uses better algorithms for scaling.
Try using this pygame function:
pygame.transform.rotozoom(Surface, angle, scale)
I also had some issues with pixilation in a game but it seemed to work with this.
It has been several weeks since I started learning python (via anaconda). I have started to develop my personal code for some applications for my work.
So the question is the next one, I have this two function draw_rectangle and magnification which each one uses a different mouse_handler.
As you can see in the code, both functions are the same, so what I want to do is to merge them into just one function (trying to make a better code structure).
Finally, I call these functions in another script via:
data['image_right'], rectangle['right'] = draw_rectangle(data['image_right'])
magnification = magnification(data['image_left'])
Here are the functions :
def mouse_handler(action, x, y, flags,img) :
# Action to be taken when left mouse button is pressed
if action==cv2.EVENT_LBUTTONDOWN:
rectangle.append([x,y])
# Action to be taken when left mouse button is released
elif action==cv2.EVENT_LBUTTONUP:
rectangle.append([x,y])
def draw_rectangle(img):
global rectangle
rectangle = []
#Create the window to show image
cv2.namedWindow("Image",cv2.WINDOW_NORMAL)
cv2.setMouseCallback("Image", mouse_handler, img)
cv2.imshow("Image", img)
cv2.waitKey(0)
cv2.destroyAllWindows()
img = img[rectangle[0][1]:rectangle[1][1],rectangle[0][0]:rectangle[1][0]]
return img, rectangle
def mouse_handler2(action, x, y, flags,img) :
# Action to be taken when left mouse button is pressed
if action==cv2.EVENT_LBUTTONDOWN:
coordinates.append([x,y])
# Action to be taken when left mouse button is released
elif action==cv2.EVENT_LBUTTONUP:
coordinates.append([x,y])
def magnification(img):
global coordinates
coordinates = []
imgcopy = img.copy()
#Create the window to show image
cv2.namedWindow("Image",cv2.WINDOW_NORMAL)
cv2.setMouseCallback("Image", mouse_handler2, imgcopy)
cv2.putText(imgcopy, "Click/unclick", (int(imgcopy.shape[1]/4), int(imgcopy.shape[0]/2)), cv2.FONT_HERSHEY_SIMPLEX, 3, (0, 0, 0), 3)
cv2.imshow("Image", imgcopy)
cv2.waitKey(0)
cv2.destroyAllWindows()
magnification = 10/np.sqrt( np.power((coordinates[0][1]-coordinates[1][1]),2)+ np.power((coordinates[0][0]-coordinates[1][0]),2))
return magnification
I'm using raspberry pi3 and I have written hand recognition code using OpenCV with python. But now I want to print text like "left to right" on its live video stream screen, when hand is moving from left to right and print text like "right to left", when hand is moving from right to left.
Now my question is using cv2.putText() we can display text but how do I find the direction that hand is moved from left to right or vice-versa?
Anyone have idea how to display this text this? Please reply. Thanks in advance.
If I understand it correctly, you are already recognising the hand (the Region of Interest aka ROI) and just wonder how to know if it moves left or right. In order to recognise this, you should keep some history of its location. Just remember for a few frames where the hand was.
import numpy as np
from collections import deque
cx_hist = deque(maxlen=10) # how many frames history
while True:
...
M = cv2.moments(contour) # the moment of one contour
cx = int(M['m10']/M['m00']) # the x coordinate of the centroid
cx_hist.append(cx)
diff = [cx_hist[i+1]-cx_hist[i] for i in range(len(cx_hist)-1)]
treshold = 2 # some treshold of significant (pixel) movement
if np.mean(diff) > treshold:
print('positive means movement to the right')
elif np.mean(diff) < treshold*-1:
print('negative means movement to the left')
else:
print('below the tresholds there is little movement')
You have to transform it to puttext yourself. I used the centroid coordinates, but you could pick something else. See http://docs.opencv.org/3.1.0/dd/d49/tutorial_py_contour_features.html
To apply boundary fill in a region i need to draw a free hand shape(random) using mouse in python-opencv
You asked how to draw any giver random shape on a picture using your computer's mouse. Here is a simple solution:
First, you will need to design a method that enables you to draw. So let's inspire ourselves from OpenCV: Mouse as a Paint-Brush where a method is used to draw common regular shapes such as a circle or a rectangle using a mouse. In your case, you will need random drawing as you could do with your hand.
So using that method you can draw points using the mouse and perform an interpolation between them using cv2.line() method:
cv2.line(im,(current_former_x,current_former_y),(former_x,former_y),(0,0,255),5)
Where im is the image you read and while you must memorize the former coordinates of the mouse position all the time:
current_former_x = former_x
current_former_y = former_y
Full OpenCV program:
Here is the code. Do not hesitate to comment anything you wouldn't understand:
'''
Created on Apr 3, 2016
#author: Bill BEGUERADJ
'''
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
# mouse callback function
def begueradj_draw(event,former_x,former_y,flags,param):
global current_former_x,current_former_y,drawing, mode
if event==cv2.EVENT_LBUTTONDOWN:
drawing=True
current_former_x,current_former_y=former_x,former_y
elif event==cv2.EVENT_MOUSEMOVE:
if drawing==True:
if mode==True:
cv2.line(im,(current_former_x,current_former_y),(former_x,former_y),(0,0,255),5)
current_former_x = former_x
current_former_y = former_y
#print former_x,former_y
elif event==cv2.EVENT_LBUTTONUP:
drawing=False
if mode==True:
cv2.line(im,(current_former_x,current_former_y),(former_x,former_y),(0,0,255),5)
current_former_x = former_x
current_former_y = former_y
return former_x,former_y
im = cv2.imread("darwin.jpg")
cv2.namedWindow("Bill BEGUERADJ OpenCV")
cv2.setMouseCallback('Bill BEGUERADJ OpenCV',begueradj_draw)
while(1):
cv2.imshow('Bill BEGUERADJ OpenCV',im)
k=cv2.waitKey(1)&0xFF
if k==27:
break
cv2.destroyAllWindows()
Demo:
This example from the opencv sample directory allows you to draw an arbitrary rectangle in an image and select the ROI:
https://github.com/Itseez/opencv/blob/master/samples/python/mouse_and_match.py
You can easily add alternatives to draw circles or polygons instead, e.g. by pressing a letter first.