So basically I have an app that has a splash screen Tkinter frame.
I need this frame to close when openCV detects a face, after which openCV opens a window that shows the camera feed and starts tracking smiles. The other classes initialized in the code below are for updating a database and playing a DTMF tone when a smile is recognized, my main issue is that the mainloop from the Tkinter frame won't allow any other processes to continue.
I also need the Tkinter frame to close when a face is detected. Right now I tried setting it up with a separate face detection loop dedicated just to closing the frame (the frames class has a facefound() function which just calls a self.destroy()).
I need to multiprocess the frame's mainloop so that the face detector can actually check whether or not a face is in view.
Current code:
import numpy as np
import sys
from imutils.video import VideoStream
import datetime
import argparse
import imutils
import time
import cv2
from chocolate_returner import Choco_returner
from given_resetter import Given_resetter
from tone_player import Tone_player
from play_flagger import Play_flagger
from Welcome_frame import Intro
import time
ap = argparse.ArgumentParser()
ap.add_argument("-p", "--picamera", type=int, default=-1,
help="whether or not the Raspberry Pi camera should be used")
args = vars(ap.parse_args())
faceCascade = cv2.CascadeClassifier('faces.xml')
smileCascade = cv2.CascadeClassifier('smiles.xml')
cap = VideoStream(usePiCamera=args["picamera"] > 0).start()
time.sleep(2.0)
while True:
framex = cap.read() # Capture frame-by-frame
frame = imutils.resize(framex, width=400)
gray = cv2.cvtColor(frame, cv2.COLOR_BGR2GRAY)
faces = faceCascade.detectMultiScale(
gray,
scaleFactor=1.1,
minNeighbors=5,
minSize=(30, 30),
flags=0
)
# ---- 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)
print "found ", len(faces), "faces"
roi_gray = gray[y:y + h, x:x + w]
roi_color = frame[y:y + h, x:x + w]
cv2.imshow('Video', frame)
if cv2.waitKey(1) & 0xFF == ord('q'):
break
if (len(faces) > 0):
check = True
it.facefound()
break
while True:
framex = cap.read() # Capture frame-by-frame
frame = imutils.resize(framex, width=400)
gray = cv2.cvtColor(frame, cv2.COLOR_BGR2GRAY)
faces = faceCascade.detectMultiScale(
gray,
scaleFactor= 1.1,
minNeighbors=5,
minSize=(30, 30),
flags=0
)
# ---- 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)
print "found ", len(faces), "faces"
roi_gray = gray[y:y+h, x:x+w]
roi_color = frame[y:y+h, x:x+w]
smile = smileCascade.detectMultiScale(
roi_gray,
scaleFactor= 1.7,
minNeighbors=5,
minSize=(5, 5),
flags=0
)
time.sleep(2.0)
# Set region of interest for smiles
for (x, y, w, h) in smile:
print "Found", len(smile), "smiles!"
cv2.rectangle(roi_color, (x, y), (x+w, y+h), (255, 0, 0), 1)
img = framex
cv2.imwrite("test.jpg", img)
cr = Choco_returner()
gr = Given_resetter()
tp = Tone_player()
pf = Play_flagger()
if (len(smile) > 0):
return1 = cr.returner()
tp.play(str(return1[1]), str(return1[2]))
pf.flag(return1[0])
#print "!!!!!!!!!!!!!!!!!"
# Display the resulting frame
cv2.imshow('Video', frame)
if cv2.waitKey(1) & 0xFF == ord('q'):
break
cap.release()
cv2.destroyAllWindows()
Related
I'm trying to build a laser shot detection game in Python3 using OpenCV. I have a proof of concept working which will highlight detected momentary "shots" of the laser on the background.
The problem I'm having is that the laser signature doesn't get detected on lighter backgrounds, or if there is a very white / bright color item near the shot. I'm sure this is because the way I'm using binary thresholds to detect the laser as the brightest thing in the frame, and with light elements the laser gets washed out.
My question is how can I alter my approach to handle this situation, or perhaps "calibrate" the background / other items so that the laser can be detected? Ultimately I'm trying to detect laser shots on a computer screen, where the background where shots are landing is a video with its own high lights and the screen puts out its own light.
Any guidance is appreciated.
main.py
import cv2
from camera import VideoCamera
from detection import LaserDetector
debug=False
radius_min = float(1)
radius_max = float(10)
shot_size = 5
color_blue = (255, 0, 0)
cam = VideoCamera(640, 480)
shots = []
try:
while(True):
frame = cam.get_frame()
if frame is not False:
shot = LaserDetector(frame, debug).detect()
if shot:
x, y, radius = shot
if radius >= radius_min and radius <= radius_max:
shots.append(shot)
if debug:
print(f"(x:{int(x)}, y:{int(y)}, r:{int(radius)})")
for s in shots:
cv2.circle(frame, (int(s[0]), int(s[1])), shot_size, color_blue, 1)
cv2.imshow('frame', frame)
if cv2.waitKey(1) & 0xFF == ord('q'):
break
except KeyboardInterrupt:
pass
finally:
print("\nClosing video capture...")
cam.release()
cv2.destroyAllWindows()
camera.py
import cv2
class VideoCamera(object):
def __init__(self, cam_width, cam_height):
self.capture = cv2.VideoCapture(0)
self.capture.set(cv2.CAP_PROP_FRAME_WIDTH, cam_width)
self.capture.set(cv2.CAP_PROP_FRAME_HEIGHT, cam_height)
def release(self):
self.capture.release()
def __del__(self):
self.capture.release()
def get_frame(self):
success, frame = self.capture.read()
if success:
return frame
else:
return False
def frame_to_jpeg(self, frame):
ret, jpeg = cv2.imencode('.jpg', frame)
return jpeg.tobytes()
detection.py
import cv2
import numpy as np
import decimal
class LaserDetector(object):
def __init__(self, frame, debug=False):
self.debug = debug
self.frame = frame
def get_contour_points(self, mask):
countours = cv2.findContours(mask, cv2.RETR_EXTERNAL, cv2.CHAIN_APPROX_SIMPLE)[-2]
if len(countours) > 0:
# find the largest contour in the mask, then use
# it to compute the minimum enclosing circle and
c = max(countours, key=cv2.contourArea)
((x, y), radius) = cv2.minEnclosingCircle(c)
moments = cv2.moments(c)
if moments["m00"] > 0:
# set the center
(x,y) = int(moments["m10"] / moments["m00"]), \
int(moments["m01"] / moments["m00"])
radius = round(decimal.Decimal(radius), 3)
return (int(x), int(y), radius)
return False
def get_hsv_threshold_mask(self, frame):
hsv_image = cv2.cvtColor(frame, cv2.COLOR_BGR2HSV)
h, s, v = cv2.split(hsv_image)
ret, h_frame = cv2.threshold(h, 125, 160, cv2.THRESH_BINARY)
ret, v_frame = cv2.threshold(v, 250, 256, cv2.THRESH_BINARY)
output = cv2.bitwise_and(h_frame, v_frame, frame)
if self.debug:
indiv_output = np.concatenate((h_frame, v_frame, output), axis=1)
cv2.imshow("threshold", indiv_output)
return output
def detect(self):
mask = self.get_hsv_threshold_mask(self.frame)
return self.get_contour_points(mask)
I am using a Rapberry Pi to videostream in a web server.
The video streaming works fine, but once I implement the face recognition, it will be so laggy.
This is my code :
from flask import Flask, render_template, Response
import cv2
import argparse
from imutils.video import VideoStream
import imutils
import subprocess
app = Flask(__name__)
# Load the cascade
face_cascade = cv2.CascadeClassifier('haarcascade_frontalface_default.xml')
vs = VideoStream(usePiCamera=1).start()
def gen_frames(): # generate frame by frame from camera
while True:
# Capture frame-by-frame
global vs
frame = vs.read() # read the camera frame
frame = imutils.resize(frame, width=800)
gray = cv2.cvtColor(frame, cv2.COLOR_BGR2GRAY)
# Detect the faces
faces = face_cascade.detectMultiScale(gray, 1.1, 4)
#Draw the rectangle around each face
for (x, y, w, h) in faces:
cv2.rectangle(frame, (x, y), (x+w, y+h), (255, 0, 0), 2)
ret, buffer = cv2.imencode('.jpg', frame)
frame = buffer.tobytes()
yield (b'--frame\r\n'b'Content-Type: image/jpeg\r\n\r\n' + frame + b'\r\n') # concat frame one by one and show result
#app.route('/video_feed')
def video_feed():
#Video streaming route. Put this in the src attribute of an img tag
return Response(gen_frames(), mimetype='multipart/x-mixed-replace; boundary=frame')
#app.route('/')
def index():
"""Video streaming home page."""
return render_template('index.html')
def videostreaming():
# start the flask app
app.run(host="192.168.0.33",port=8000, debug=True,use_reloader=False)
videostreaming()
Is there a way to make the videostreaming smoother with the facedetection feature ?
It seems that the width of the video was the one causing problems in my videostreaming. Just had to reduce it to 500 to make it smoother.
From :
frame = imutils.resize(frame, width=800)
To :
frame = imutils.resize(frame, width=500)
import cv2
import numpy as np
import pyautogui
SCREEN_SIZE = (1920, 1080)
#define the codec
fourcc = cv2.VideoWriter_fourcc(*"XVID")
#create the video write object
out = cv2.VideoWriter("output.avi", fourcc, 20.0, (SCREEN_SIZE))
while True:
#make a screenshot
img = pyautogui.screenshot(region=(680, 785, 560, 20))
#convert these pixels to a proper numpy array to work with OpenCV
frame = np.array(img)
#convert colors from BGR to RGB
frame = cv2.cvtColor(frame, cv2.COLOR_BGR2RGB)
black = [0,0,0]
for x in range(img.width):
for y in range(img.height):
if img.getpixel((x, y)) == black:
print(x, y)
pyautogui.click(x, y)
#write the frame
out.write(frame)
#show the frame
cv2.imshow("screenshot", frame)
# if the user clicks q, it exits
if cv2.waitKey(1) == ord("q"):
break
# make sure everything is closed when exited
cv2.destroyAllWindows()
out.release()
I am creating a script to detect black squares, and click them. For some reason when using this, there is no error, but it is not clicking. Is there a way to tell whether it is detecting the color/clicking?
Edit: It does not output the coordinates, and when changing it to print "black" once finding the color, there is still no output.
I only started learning to program 3 months ago, so I am sorry if this question sounds stupid.
What I am trying to do here is to make my computer click left button every 5 seconds without pausing the while loop so it keeps reading the screen.
I tried to use time.sleep(5) (but it pauses the function) and this 'py.click(x, y, clicks=2, interval=5)' does the same thing as well.
import numpy as np
from PIL import ImageGrab
import cv2
import time
import pyautogui as py
def screen_record():
while True:
printscreen1 = np.array(ImageGrab.grab(bbox=(0, 40, 800, 600)))
printscreen2 = np.array(ImageGrab.grab(bbox=(0, 40, 800, 600)))
diff = cv2.absdiff(printscreen1, printscreen2)
grey = cv2.cvtColor(diff, cv2.COLOR_BGR2GRAY)
blur = cv2.GaussianBlur(grey, (5, 5), 0)
_, thresh = cv2.threshold(blur, 20, 255, cv2.THRESH_BINARY)
dilated = cv2.dilate(thresh, None, iterations=3)
contours, _ = cv2.findContours(dilated, cv2.RETR_TREE, cv2.CHAIN_APPROX_SIMPLE)
for contour in contours:
(x, y, w, h) = cv2.boundingRect(contour)
if cv2.contourArea(contour) < 700:
continue
else:
# py.click(x, y, clicks=2, interval=5)
# time.sleep(5)
cv2.rectangle(printscreen1, (x, y), (x + w, y + h), (0, 255, 0), 2)
# cv2.drawContours(printscreen1, contours, -1, (0, 255, 0), 2)
cv2.imshow('feed', printscreen1)
if cv2.waitKey(25) & 0xFF == ord('q'):
cv2.destroyAllWindows()
break
screen_record()
you could use for example threading.Timer
import threading
import time
run = True
def main_loop():
global run
while run:
try:
time.sleep(1)
print('main loop')
except KeyboardInterrupt:
run = False
print('main loop FIN')
break
def fn_start():
global run
if run:
print('Click')
s_timer()
else:
print('timer FIN')
return
def s_timer():
t = threading.Timer(5.0, fn_start)
t.start()
s_timer()
main_loop()
I am using the following code on python 3.7
import cv2
import numpy as np
face_cascade = cv2.CascadeClassifier('D:\\ET\\haarcascade_frontalface_default.xml')
eye_cascade = cv2.CascadeClassifier('D:\\ET\\haarcascade_eye.xml')
cap = cv2.VideoCapture(0)
while True:
ret, img = cap.read()
gray = cv2.cvtColor(img, cv2.COLOR_BGR2GRAY)
faces = face_cascade.detectMultiScale(gray, 1.3, 5)
for (x,y,w,h) in faces:
cv2.rectange(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]
eyes = eye_cascade.detectMultiScale(roi_gray)
for (ex,ey,ew,eh) in eyes:
cv2.rectangle(roi_color,(ex,ey), (ex+ew,ey+eh),(0,255,0),2)
cv2.imshow('img',img)
k = cv2.waitKey(30) & 0xFF
if k == 27:
break
cap.release()
cv2.destroyAllWindows()
the following is installed:
opencv-contrib-python-3.4.4.19
The error is telling you that cv2 doesn't have anything called rectange, but it does have something called rectangle. You misspelled it:
cv2.rectange(img,(x,y),(x+w,y+h),(255,0,0),2)