Emulate keyboard pressing from one infinite loop to another in a GUI - python

I’m trying to do a GUI where the user is gonna control the buttons by eyeblinking. Basically, a short blink should emulate pressing the keyboard key Tab (to move from one button to another) and a long blink should emulate pressing the key Space (to enter in the selected button).
The idea is that both processes, the window and the eyeblink detection system, run at the same time. So here I get all the problems: as they are both while loops I cannot run them at the same time.
In the code I attach, I simplify this by opening first the main window and afterwards clicking the button Start to run the eyeblink system. With pyautogui.press() I suppose to emulate the keyboard pressing in the main window. However, when the eyeblink detection system is working, the main window is no longer accessible (you cannot press anything).
I have tried to evoke the blink function every frame instead of an endless loop, but it’s too slow and not able to properly detect the blinks. I’ve also tried multiprocessing and ‘Python quits unexpectadly’, no error shown so not sure what’s going on (the code I used to try this is at the end commented). I also tried threading but in a simple way an no error but nothing appears either (again, the code I used to try this is at the end commented).
Here I attach the link to the files (.mp3, .xml, .py):
https://drive.google.com/drive/folders/1U2uwHXzl2MtSTlAKw1L68L3xcRmelP2d?usp=sharing
I’ve just started using Python so my knowledge is not high, I’m running out of time and I’m stuck at this point… So any help would be welcome!! Thanks in advance ;)
MacOs
Python 2.7
OpenCV 3.4
Tkinter (I just chose it because it is easy to handle yet I’m open to change if it’s neccessary)
# Ventana+Blink
from Tkinter import *
import numpy as np
import cv2
# To emulate a keyboard pressing
import pyautogui
import time
# To play the sounds
import subprocess
# from Blink import funcion_blink
# from multiprocessing import Process
# import threading
def Onbutton_clicked():
# while True:
# Repeating 2 times the sound
for x in range (0,2):
subprocess.call(['afplay', 'alarm2.mp3'])
def Onbutton2_clicked():
# Repeating 1 times the sound
for x in range (0,1):
subprocess.call(['afplay', 'sound.mp3'])
def execute_func1():
print('enter\n')
pyautogui.press('space') # press the Space key
for x in range (1,2):
subprocess.call(['afplay', 'unconvinced.mp3'])
def execute_func2():
print('tab\n')
pyautogui.press('tab') # press the Tab key
for x in range (1,2):
subprocess.call(['afplay', 'case-closed.mp3'])
def execute_func3():
print('space\n')
pyautogui.press('space') # press the Space key
for x in range (1,2):
subprocess.call(['afplay', 'cheerful.mp3'])
# ----- Eyeblink detection system -----
def funcion_blink():
#XML classifiers should be in the folder with this file
face_cascade = cv2.CascadeClassifier('haarcascade_frontalface_default.xml')
eye_cascade = cv2.CascadeClassifier('haarcascade_eye.xml')
video_capture = cv2.VideoCapture(0)
det = 0
n = 0
while True:
# Capture frame-by-frame
ret, frame = video_capture.read()
gray = cv2.cvtColor(frame, cv2.COLOR_BGR2GRAY)
faces = face_cascade.detectMultiScale(
gray,
scaleFactor=1.1,
minNeighbors=5,
minSize=(100, 100),
flags=cv2.CASCADE_SCALE_IMAGE
)
# Draw a rectangle around the faces
for (x, y, w, h) in faces:
cv2.rectangle(frame, (x, y), (x+w, y+h), (0, 255, 0), 2)
roi_gray = gray[y:y + h, x:x + w]
roi_color = frame[y:y + h, x:x + w]
eyes = eye_cascade.detectMultiScale(
roi_gray,
scaleFactor = 1.1,
minNeighbors = 5,
minSize = (30, 30),
flags = cv2.CASCADE_SCALE_IMAGE
)
for (ex, ey, ew, eh) in eyes:
cv2.rectangle(roi_color, (ex, ey), (ex + ew, ey + eh), (0, 255, 0), 2)
face_img = gray[x:x + w, y:y + h]
face_res = cv2.resize(face_img, (100, 100), interpolation=cv2.INTER_CUBIC)
eye_reg = face_res[15:85, 20:50]
cv2.rectangle(frame, (x+15*w/100, y + 2*h / 10), (x + w*85/100, y + (5 * h / 10)), (0, 0, 255), 2)
if (det < 10):
tmpl_eyes = eye_reg
det = det + 1
print('template acquired\n')
elif (det == 10):
# template matching
wt, ht = tmpl_eyes.shape[::-1]
#res_templ = cv2.matchTemplate(eye_reg, tmpl_eyes, cv2.TM_CCORR_NORMED)
res_templ = cv2.matchTemplate(eye_reg, tmpl_eyes, cv2.TM_CCOEFF_NORMED)
min_val, max_val, min_loc, max_loc = cv2.minMaxLoc(res_templ)
# print(max_val, n)
#value 0.92 should be adapted to the conditions and camera position
if (max_val>0.90):
n=n+1
else:
if (n>=12):
execute_func1()
#here should go the code that triggers some action when the person blinks??
elif (n>=6):
execute_func2()
elif (n>=3):
execute_func3()
n = 0
print(max_val, n)
# Display the resulting frame
cv2.imshow('Video', frame)
if cv2.waitKey(1) & 0xFF == ord('q'):
break
# When everything is done, release the capture
cv2.destroyAllWindows()
video_capture.release()
# ---- Main window ----
def main_window():
root= Tk()
root.geometry('700x700')
# Create the buttons of the main window
button=Button(root, text='alarm', command=Onbutton_clicked)
button.bind('<Return>', Onbutton_clicked)
button.pack()
button2=Button(root, text='extra', command=Onbutton2_clicked)
button2.bind('<Return>', Onbutton2_clicked)
button2.pack()
# By pressing this button we start running the eyeblink detection system
button3=Button(root, text='Start', command=funcion_blink)
button3.bind('<Button-1>', funcion_blink)
button3.pack()
# To maintain the window until you close it
root.mainloop()
# Execute the main window
main_window()
# ---- Trials ----
# while True:
# main_window()
# funcion_blink()
# It just plays one function and when it finishes it plays the next one
# Multiprocessing
# if __name__ == '__main__':
# Process(target=main_window).start()
# Process(target=funcion_blink).start()
# PYTHON QUITS UNEXPECTADLY
# Threading
# p1 = threading.Thread(target=main_window, args=())
# p2 = threading.Thread(target=funcion_blink, args=())
# p1.start()
# p2.start()

Related

How to Get Mouse Coordinates on Click AND Move to Next Picture On Click

I got a quick script from GeeksforGeeks that allows me to get the coordinates from a mouse click on an image. That is below:
# importing the module
import cv2
# function to display the coordinates of
# of the points clicked on the image
def click_event(event, x, y, flags, params):
# checking for left mouse clicks
if event == cv2.EVENT_LBUTTONDOWN:
# displaying the coordinates
# on the Shell
print(x, ' ', y)
# displaying the coordinates
# on the image window
font = cv2.FONT_HERSHEY_SIMPLEX
cv2.putText(img, str(x) + ',' +
str(y), (x,y), font,
1, (255, 0, 0), 2)
cv2.imshow('image', img)
# checking for right mouse clicks
if event==cv2.EVENT_RBUTTONDOWN:
# displaying the coordinates
# on the Shell
print(x, ' ', y)
# displaying the coordinates
# on the image window
font = cv2.FONT_HERSHEY_SIMPLEX
b = img[y, x, 0]
g = img[y, x, 1]
r = img[y, x, 2]
cv2.putText(img, str(b) + ',' +
str(g) + ',' + str(r),
(x,y), font, 1,
(255, 255, 0), 2)
cv2.imshow('image', img)
# driver function
if __name__=="__main__":
current = 94
# reading the image
img = cv2.imread('C:/Users/jcoli/Downloads/Data/' + str(current) + '.png', 1)
# displaying the image
cv2.imshow('image', img)
# setting mouse handler for the image
# and calling the click_event() function
cv2.setMouseCallback('image', click_event)
# wait for a key to be pressed to exit
cv2.waitKey(0)
# if cv2.waitKey(0) & 0xFF == 27:
# break
# close the window
cv2.destroyAllWindows()
However, I would like to click a button to move to the next picture. To do this, all I need to do is to update current by +1 because only the numbers are different from each frame. I understand that I need to do something like this:
key_pressed = ""
if key_pressed == ord('d'):
current += 1
key_pressed = ""
But, I don't know where I need to put it or if this is even correct. Any help on solving this would be greatly appreciated!

How to call a function only once and wait for 5 seconds before calling again inside while loop- Python

I'm trying capture image and play a song when smile is detected on live camera feed. I have created a separate thread to play complete song and save image using multi-threading as frames were getting stuck when song was playing.
The issue I am facing is that inside the infinite while loop, the multi-thread function is being called multiple times in a second resulting in overlapping of the song and many images being saved.
Is there a better way to call that function once and wait for few seconds (5 seconds exactly) till song is finished without breaking/pausing the while loop?
Here is the code I have worked on:
import cv2
import datetime
import threading
from playsound import playsound
def play_save(save_img):
print("Saving Image")
time_stamp = datetime.datetime.now().strftime('%Y-%m-%d-%H-%M-%S')
file_name = f'selfie-{time_stamp}.png'
cv2.imwrite(file_name, save_img)
print("Playing Song")
playsound("Happy_birthday.mp3")
def main():
face_cascade = cv2.CascadeClassifier('haarcascade_frontalface_default.xml')
smile_cascade = cv2.CascadeClassifier('haarcascade_smile.xml')
cap = cv2.VideoCapture(0)
while True:
ret, img = cap.read()
frame = img.copy()
if not ret:
break
gray = cv2.cvtColor(img, cv2.COLOR_BGR2GRAY)
faces = face_cascade.detectMultiScale(gray, 1.3, 5)
for (x, y, w, h) in faces:
cv2.rectangle(img, (x, y), (x + w, y + h), (255, 0, 0), 2)
roi_gray = gray[y:y + h, x:x + w]
roi_color = img[y:y + h, x:x + w]
smile = smile_cascade.detectMultiScale(roi_gray, 1.3, 25)
for x1, y1, w1, h1 in smile:
cv2.rectangle(img, (x1, y1), (x1 + w1, y1 + h1), (0, 0, 255), 2)
multithread = threading.Thread(target=play_save, args=(frame,))
multithread.start()
cv2.imshow('Smile Birthday', img)
k = cv2.waitKey(30) & 0xff
if k == 27:
break
cap.release()
cv2.destroyAllWindows()
if __name__ == "__main__":
main()
An Idea is to use the UTC-Time to check it. You could make a check like this:
import time
time_function_done = None
while True:
#Do some shit
if (time_function_done + 5) < time.time():
time_function_done = time.time()
#Do your function
#Do some other shit
If you can't find a direct solution, make a workaround. Hope this helps :))
import time
time.sleep(5) # Sleep for 5 seconds

Killing a child thread running a multiprocessing operation

I'm new to the multiprocessing thing. I was designing an app where the user can select a video and the app would do facial recognition on it and show it. I used concurrent.futures.ProcessPoolExecutor to multiprocess the frames to improve the total time required. My problem is when I need to integrate the face recognition multiprocessing part in the app, I would need to run the entire thing on a separate thread to keep it responsive.
Usually when the stand alone multiprocessing part runs, it can be stopped by raising an exception. BUt while running on a separate thread, they have their own stack. I have seen people suggesting using a message to verify if the code should keep running or not. But my caller function is not running on a "while loop" to check that.
All i'm asking is, if there is any way I can raise an exception to stop both threads or any other way to stop running that child thread from the main thread. My code is attached below. The sub() is the caller function and the process() is the function that is used as the multiprocessing target.
Thanks in advance.
import cv2
import face_recognition
import numpy as np
import time
import pickle
import concurrent.futures
import queue
import file_video_stream
def process(sframe, facesencoded, knownfacenames,process_this_frame):
if sframe is not None :
if process_this_frame:
small_frame = cv2.resize(sframe, (0, 0), fx=0.5, fy=0.5)
rgb_small_frame = small_frame[:, :, ::-1]
face_locations = face_recognition.face_locations(rgb_small_frame)
unknown_face_encodings = face_recognition.face_encodings(rgb_small_frame, face_locations)
face_names = []
for face_encoding in unknown_face_encodings:
matches = face_recognition.compare_faces(facesencoded, face_encoding)
name = "Unknown"
face_distances = face_recognition.face_distance(facesencoded, face_encoding)
best_match_index = np.argmin(face_distances)
if matches[best_match_index]:
name = knownfacenames[best_match_index]
face_names.append(name)
for (top, right, bottom, left), name in zip(face_locations, face_names):
# Scale back up face locations since the frame we detected in was scaled to 1/4 size
top *= 2
right *= 2
bottom *= 2
left *= 2
# Draw a box around the face
cv2.rectangle(sframe, (left-20, top-20), (right+20, bottom+20), (255, 0, 0), 2)
# Draw a label with a name below the face
cv2.rectangle(sframe, (left-20, bottom -15), (right+20, bottom+20), (255, 0, 0), cv2.FILLED)
font = cv2.FONT_HERSHEY_DUPLEX
cv2.putText(sframe, name, (left -20, bottom + 15), font, 1.0, (255, 255, 255), 2)
show_frame = cv2.resize(sframe, (1080,720))
return show_frame
else :
show_frame = cv2.resize(sframe, (1080,720))
return show_frame
def sub():
list_original = []
frame_number = 0
alt_frame_number = 0
proc_frame = 0
q1= queue.Queue()
with open("known.pickle", 'rb') as ki:
faces = pickle.load(ki)
faces_encoded = list(faces.values())
known_face_names = list(faces.keys())
fvs = file_video_stream.FileVideoStream('Attendance.mp4')
fvs.start()
time.sleep(1.0)
process_this_frame = True
with concurrent.futures.ProcessPoolExecutor() as executor:
while fvs.more():
#for _ in range (5):
frame = fvs.read()
time.sleep(0.1)
list_element = executor.submit(process,frame,faces_encoded,known_face_names,process_this_frame)
time.sleep(0.1)
if list_element is not None:
frame_number +=1
#print (frame_number)
list_original.append(list_element)
else :
fvs.stop()
break
process_this_frame = not process_this_frame
print ("Total number of frames read:",frame_number)
#print ("Total number of frames processed:",alt_frame_number)
fvs.stop()
for res in list_original:
q1.put(res.result())
while not q1.empty():
dump = q1.get()
cv2.imshow('Video', dump)
time.sleep(0.01)
# Hit 'q' on the keyboard to quit!
if cv2.waitKey(1) & 0xFF == ord('q'):
break
cv2.destroyAllWindows()
if __name__ == '__main__':
sub()

Tkinter windows fails to update after using OpenCV2 (facial recognition)

I'm working on both Tkinter and OpenCV 2 (facial recognition). I'm making a program that when you press a single button (from Tkinter), the program start the recognition process, and when the user press "Escape" the recognition process stop and in the tkinter windows appears a new label indicating who the subject is.
This last thing isn't working. When I press "Escape", to finish the recognition process, the tkinter windows isn't updated with the new label.
The code is pretty large but here it is:
import cv2, sys, numpy, os
from Tkinter import *
global size
global fn_haar
global fn_dir
global Welcome
os.system("sudo modprobe bcm2835-v4l2")
size = 5
fn_haar = '/usr/share/opencv/haarcascade/haarcascade_frontalface_alt.xml'
fn_dir = '/home/pi/Control'
def recognition():
(images, lables, names, id) = ([], [], {}, 0)
subdir = "Subject 1"
names[id] = subdir
subjectpath = os.path.join(fn_dir, subdir)
for filename in os.listdir(subjectpath):
f_name, f_extension = os.path.splitext(filename)
if(f_extension.lower() not in ['.png','.jpg','.jpeg','.gif','.pgm']):
continue
path = subjectpath + '/' + filename
lable = id
images.append(cv2.imread(path, 0))
lables.append(int(lable))
(im_width, im_height) = (112, 92)
(images, lables) = [numpy.array(lis) for lis in [images, lables]]
model = cv2.createLBPHFaceRecognizer()
model.train(images, lables)
haar_cascade = cv2.CascadeClassifier(fn_haar)
webcam = cv2.VideoCapture(0)
while True:
rval = False
while(not rval):
(rval, frame) = webcam.read()
if(not rval):
print "Failed to open webcam. Trying again..."
frame=cv2.flip(frame,1,0)
gray = cv2.cvtColor(frame, cv2.COLOR_BGR2GRAY)
mini = cv2.resize(gray, (int(gray.shape[1] / size), int(gray.shape[0] / size)))
faces = haar_cascade.detectMultiScale(mini)
for i in range(len(faces)):
face_i = faces[i]
(x, y, w, h) = [v * size for v in face_i]
face = gray[y:y + h, x:x + w]
face_resize = cv2.resize(face, (im_width, im_height))
prediction = model.predict(face_resize)
cv2.rectangle(frame, (x, y), (x + w, y + h), (0, 255, 0), 3)
if prediction[1]<90:
cv2.putText(frame,'%s - %.0f' %(names[prediction[0]],prediction[1]),(x-10, y-10),cv2.FONT_HERSHEY_PLAIN,1,(0, 255, 0))
else:
cv2.putText(frame,'Unknown',(x-10, y-10),cv2.FONT_HERSHEY_PLAIN,1,(0, 255, 0))
cv2.imshow('OpenCV', frame)
cv2.moveWindow('OpenCV', 350, 120)
key = cv2.waitKey(10)
if key == 27:
cv2.startWindowThread()
cv2.destroyWindow('OpenCV')
L1 = Label(Welcome, text = "The subject is:" + names[prediction[0]]).pack()
Welcome = Tk()
Welcome.title ("Welcome")
Welcome.geometry ("300x100+1+1")
LabelWelcome = Label(Welcome, text = "Welcome to the facial recognitio program").pack()
B1 = Button(Welcome, text="START", command=recognition).pack()
Welcome.mainloop()
The program runs fine until I press "Escape", and then it doesn't update the windows with the new label and the windows just freezes.
So, after doing some tests I think the problem is that "cv2.destroyWindow('OpenCV')" destroy the mainloop or something like that.
My questions are:
Is that the problem ? cv2.destroyWindow('OpenCV') destroy the mainloop too?
If that is the problem, what can I do about that?
If thats not the problem do you know what can be?
PD: I'm thinking that maybe, if the problem is with cv2.destroyWindow, I can just hide that windows (openCV) and not close it (don't know if this is possible).
PD2: I know that if this code works, I'm going to add a label every time I run the recognition part, but I'm ok with that for the moment.
Thanks in advance.

opencv window not closing after calling destroyAllWindows() function

I am implementing camshift algorithm in python using opencv and using the position given by it to move my mouse and draw on the kolour application.
When i press the button on in tkinter ui a frame launches and then
Upon pressing the key 'q' the 'frame' does not close, The frame just freezes and the option to force quit comes. I am using tkinter for UI.
global paint_open
paint_open = 0
def func():
global frame, roiPts, inputMode ,token
camera = cv2.VideoCapture(0)
cv2.namedWindow("frame")
cv2.setMouseCallback("frame", selectROI)
termination = (cv2.TERM_CRITERIA_EPS | cv2.TERM_CRITERIA_COUNT, 10, 1)
roiBox = None
li=[]
while True:
(grabbed, frame) = camera.read()
if roiBox is not None:
hsv = cv2.cvtColor(frame, cv2.COLOR_BGR2HSV)
backProj = cv2.calcBackProject([hsv], [0], roiHist, [0, 180], 1)
(r, roiBox) = cv2.CamShift(backProj, roiBox, termination)
pts = np.int0(cv2.cv.BoxPoints(r))
li.append(pts)
#coordinates contain the coordinates of the tracked object
coordinates = r[0]
# x, y contains the coordinates
x = int(coordinates[0])
y = int(coordinates[1])
drawing(x,y)
#draws a circle around the center from x,y
cv2.circle(frame, (int(x), int(y)), 4, (0, 0, 255), 2)
#draws a colored frame around the object
cv2.polylines(frame, [pts], True, (255, 0, 0), 2)
#here imshow function start
cv2.imshow("frame", frame)
key = cv2.waitKey(1) & 0xFF
# handle if the 'i' key is pressed, then go into ROI
if key == ord("i") and len(roiPts) < 4:
inputMode = True
orig = frame.copy()
while len(roiPts) < 4:
cv2.imshow("frame", frame)
cv2.waitKey(0)
roiPts = np.array(roiPts)
s = roiPts.sum(axis = 1)
tl = roiPts[np.argmin(s)]
br = roiPts[np.argmax(s)]
roi = orig[tl[1]:br[1], tl[0]:br[0]]
roi = cv2.cvtColor(roi, cv2.COLOR_BGR2HSV)
roiHist = cv2.calcHist([roi], [0], None, [16], [0, 180])
roiHist = cv2.normalize(roiHist, roiHist, 0, 255, cv2.NORM_MINMAX)
roiBox = (tl[0], tl[1], br[0], br[1])
# if the 'q' key is pressed, stop the loop
elif key == ord("q"):
break
camera.release()
cv2.destroyAllWindows()
def drawing(x,y):
global paint_open
if paint_open == 0:
launchapp('kolourpaint')
paint_open = 1
py.dragTo(x,y)
def main():
root=tk.Tk()
bkgcolor='#D84315'
root.configure(background=bkgcolor)
label=tk.Label(root,text="Gesture Recognition Project",font=('arial black',12),fg="white",width=90,height=3,bg="purple")
label.pack()
frame1 = tk.Frame(root,bg=bkgcolor)
frame1.pack(padx=205,pady=25)
bframe1 = tk.Frame(root,bg=bkgcolor)
bframe1.pack(side=tk.BOTTOM,padx=205)
photo3 = tk.PhotoImage(file="3.png")
button2 = tk.Button(frame1, width=255, height=255, image=photo3,text="Slide Presenter",fg="purple",bg="white",command=func)
button2.pack(side=tk.LEFT,padx=25)
button2.image = photo1
# start the event loop
root.mainloop()
This question is a bit old but worth answering, since it comes up in searches. I experienced the same problem and eventually found an answer that worked. It seems hack-ish, but allows opencv to go through its processes to close a window. Opencv performs its processing during the waitKey(), so adding a few of those before and after the destroyallwindows() function did the trick.
In your code, where it says
cv2.destroyAllWindows()
... try this instead. It worked for me.
# All these waitkeys are a hack to get the OpenCV window to close
cv2.waitKey(1)
cv2.destroyAllWindows()
for i in range (1,5):
cv2.waitKey(1)
return
I'm not sure but cv may use tkinter to display windows so root.mainloop() may keep windows open. You can try to end root.mainloop() to close program and all windows.
import Tkinter as tk
import cv2
# create global value (with some value)
root = None
def func():
# inform function to use global variable instead of local one
global root
# ...
cv2.destroyAllWindows()
camera.release()
# close root window and leave mainloop()
root.destroy()
def main():
# inform function to use global variable instead of local one
global root
root = tk.Tk()
# ...
root.mainloop()
main()
Since a new window is opened upon calling destroyAllWindows() function, you need to press space-key to close the execution of the command.
This work for me
while True:
_, frame = cap.read()
cv2.imshow(windowName, frame)
keyCode = cv2.waitKey(1)
if cv2.getWindowProperty(windowName, cv2.WND_PROP_VISIBLE) <1:
break
cv2.destroyAllWindows()
from https://newbedev.com/closing-video-window-using-close-x-button-in-opencv-python

Categories