How to multithreading without causing app freeze or lag? - python

Hey I started learning python not so long time ago. Right now Im creating (or more likely trying to) face and motion detection script based on OpenCV library. Unfortunately Im stuck since few days cause I cant solve problem with I guess its called multi threading.
Here is my code:
import time
import cv2
import datetime
from discord_webhook import DiscordWebhook
import threading
faceCascade = cv2.CascadeClassifier("face_recognition.xml")
# define a video capture object
video_capture = cv2.VideoCapture(0)
#writing video
frame_width = int(video_capture.get(3))
frame_height = int(video_capture.get(4))
# Define the codec and create VideoWriter object.The output is stored in 'outpy.avi' file.
out = cv2.VideoWriter(datetime.datetime.now().strftime("%A %d %B %Y %I:%M:%S%p") + '.avi',cv2.VideoWriter_fourcc('M','J','P','G'), 10, (frame_width,frame_height))
#screenshot when detectors get triggered
def screenshot():
cv2.imwrite('screenshot.png',video_capture.read()[1])
#webhook notify
def alert():
webhook = DiscordWebhook(url="", rate_limit_retry=True,
content='!ALERT!')
webhook.execute()
while(True):
# Capture the video frame by frame
ret, frame = video_capture.read()
text="not detected"
text1="not detected"
timestamp = datetime.datetime.now()
#face recognition
gray = cv2.cvtColor(frame, cv2.COLOR_BGR2GRAY)
faces = faceCascade.detectMultiScale(gray, scaleFactor=1.1, minNeighbors=5,minSize=(30, 30))
if int(format(len(faces))) > 0:
#print("Found {0} faces!".format(len(faces)))
text="detected"
else:
text="not detected"
for (x, y, w, h) in faces:
cv2.rectangle(frame, (x, y), (x+w, y+h), (0, 255, 0), 2)
#motion detection
ret, frame1 = video_capture.read()
difference = cv2.absdiff(frame, frame1) # find the difference between the frames
gray = cv2.cvtColor(difference, cv2.COLOR_BGR2GRAY)
blur = cv2.GaussianBlur(gray, (5, 5), 0)
_, thresh = cv2.threshold(blur, 20, 255, cv2.THRESH_BINARY) # create threshold
dilated = cv2.dilate(thresh, None, iterations=3)
contours, _ = cv2.findContours(dilated, cv2.RETR_TREE, cv2.CHAIN_APPROX_SIMPLE)
for c in contours:
if cv2.contourArea(c) < 5000:
continue
x, y, w, h = cv2.boundingRect(c)
cv2.rectangle(frame, (x, y), (x + w, y + h), (0, 0, 255), 2)
text1="detected"
# Display the resulting frame
ts = timestamp.strftime("%A %d %B %Y %I:%M:%S%p")
cv2.putText(frame, "Face status: {}".format(text), (10, 25), cv2.FONT_HERSHEY_SIMPLEX, 1, (0, 0, 255), 2)
cv2.putText(frame, "Motion status: {}".format(text1), (10, 50), cv2.FONT_HERSHEY_SIMPLEX, 1, (0, 0, 255), 2)
cv2.putText(frame, ts, (10, frame.shape[0] - 10), cv2.FONT_HERSHEY_SIMPLEX, 0.55, (0, 0, 255), 1)
#check if motion is detected if not change status text
if text1 == "not detected":
text1="detected"
else:
text1="not detected"
out.write(frame)
cv2.imshow('Press Q to quit', frame)
# the 'q' button is set as the
if cv2.waitKey(1) & 0xFF == ord('q'):
break
# After the loop release the cap object
video_capture.release()
out.release()
cv2.destroyAllWindows()
Im trying to send webhook and make screen shot every 3 minutes if motion is detected but for loops completely lags this app. Tried time.sleep or thread timers but it only freezes or lags app. If someone can explain me how to solve this problem I will be very thankful. Have a great day or night

Related

How to run a command once when a face was detected in OpenCV

I am trying to make it so when a face was detected, it would run a python command once instead of spamming it, for example print()
try:
import cv2
cap = cv2.VideoCapture(0)
pTime = 0
cascPath=os.path.dirname(cv2.__file__)+"/data/haarcascade_frontalface_default.xml"
faceCascade = cv2.CascadeClassifier(cascPath)
face_detected = True
while True:
success, img = cap.read()
img = cv2.flip(img, 1)
gray = cv2.cvtColor(img, cv2.COLOR_BGR2GRAY)
imgRGB = cv2.cvtColor(img, cv2.COLOR_BGR2RGB)
cTime = time.time()
fps = 1 / (cTime - pTime)
pTime = cTime
face_count = 0
cv2.putText(img, f'FPS:{int(fps)}', (20, 30), cv2.FONT_HERSHEY_SIMPLEX, 1, (0, 255, 0), 2)
faces = faceCascade.detectMultiScale(
gray,
scaleFactor=1.1,
minNeighbors=5,
minSize=(30, 30),
flags=cv2.CASCADE_SCALE_IMAGE
)
for (x, y, w, h) in faces:
cv2.rectangle(img, (x, y), (x+w, y+h), (0, 255, 0), 2)
face_count = face_count+1
cv2.putText(img, 'Face num '+str(face_count), (x-10, y-10),
cv2.FONT_HERSHEY_SIMPLEX, 0.7, (0, 0, 255), 2)
cv2.putText(img, f'Faces Detected: {face_count}', (20, 70), cv2.FONT_HERSHEY_SIMPLEX, 1, (0, 255, 0), 2)
if face_count > 0: # If one or more faces were detected
print("a face was detected")
elif face_count == 0: # If no faces were detected
print("the face magically disappeared")
cv2.imshow("Face Recognition", img)
if cv2.waitKey(1) & 0xFF == ord(' '):
cv2.destroyAllWindows()
break
except KeyboardInterrupt:
print("[KeyboardInterrupt] Exiting...")
time.sleep(2)
exit()
how can i make while the face is detected, it prints only once instead of spamming (without using something like \r)
You need to introduce state and react only when it changes.
Without copy-pasting your entire code, I'll just show you the idea.
# the state variable
seeing_faces = False
while True:
frame = get_frame()
faces = detect_faces(frame)
there_are_faces_now = (len(faces) > 0)
# compare state to current detection, different? state CHANGES
if there_are_faces_now and not seeing_faces:
print("NOW I see faces")
elif seeing_faces and not there_are_faces_now:
print("no longer seeing faces")
else:
# no change
# update state
seeing_faces = there_are_faces_now
You can track the number of detected faces every frame and execute the command you want when the number of faces increases:
try:
import cv2
cap = cv2.VideoCapture(0)
pTime = 0
cascPath=os.path.dirname(cv2.__file__)+"/data/haarcascade_frontalface_default.xml"
faceCascade = cv2.CascadeClassifier(cascPath)
face_detected = True
currentFaces = 0
while True:
success, img = cap.read()
img = cv2.flip(img, 1)
gray = cv2.cvtColor(img, cv2.COLOR_BGR2GRAY)
imgRGB = cv2.cvtColor(img, cv2.COLOR_BGR2RGB)
cTime = time.time()
fps = 1 / (cTime - pTime)
pTime = cTime
face_count = 0
cv2.putText(img, f'FPS:{int(fps)}', (20, 30), cv2.FONT_HERSHEY_SIMPLEX, 1, (0, 255, 0), 2)
faces = faceCascade.detectMultiScale(
gray,
scaleFactor=1.1,
minNeighbors=5,
minSize=(30, 30),
flags=cv2.CASCADE_SCALE_IMAGE
)
for (x, y, w, h) in faces:
cv2.rectangle(img, (x, y), (x+w, y+h), (0, 255, 0), 2)
face_count = face_count+1
cv2.putText(img, 'Face num '+str(face_count), (x-10, y-10),
cv2.FONT_HERSHEY_SIMPLEX, 0.7, (0, 0, 255), 2)
cv2.putText(img, f'Faces Detected: {face_count}', (20, 70), cv2.FONT_HERSHEY_SIMPLEX, 1, (0, 255, 0), 2)
print(f"Faces Detected: {face_count}", end="\r")
#if face_count > 0: # If one or more faces were detected
# print("a face was detected")
#elif face_count == 0: # If no faces were detected
# print("the face magically disappeared")
if face_count > currentFaces:
print("Command to execute, face detected")
currentFaces = face_count
cv2.imshow("Face Recognition", img)
if cv2.waitKey(1) & 0xFF == ord(' '):
cv2.destroyAllWindows()
break
except KeyboardInterrupt:
print("[KeyboardInterrupt] Exiting...")
time.sleep(2)
exit()

"can't open camera by index" using cv2 on Linux

I am trying to make a motion detector (using the internal camera) in python(3), I am using linux (debian), and I keep getting this error
[ WARN:0#0.724] global /io/opencv/modules/videoio/src/cap_v4l.cpp (889) open VIDEOIO(V4L2:/dev/video0): can't open camera by index
here's the code I'm using
from imutils.video import VideoStream
import argparse
import datetime
import imutils
import time
import cv2
ap = argparse.ArgumentParser()
ap.add_argument("-v", "--video", help="")
ap.add_argument("-a", "--min-area", type=int, default=500, help="minimum area size")
args = vars(ap.parse_args())
if args.get("video", None) is None:
vs = VideoStream(src=0).start()
time.sleep(2.0)
else:
vs = cv2.VideoCapture(args["Video"])
firstFrame = None
while True:
frame = vs.read()
frame = frame if args.get("video", None) is None else frame[1]
text = "Muon is stuck in helium"
if frame is None:
break
frame = imutils.resize(frame, width=500)
gray = cv2.cvtColor(frame, cv2.COLOR_BGR2GRAY)
gray = cv2.GaussianBlur(gray, (21, 21), 0)
if firstFrame is None:
firstFrame = gray
continue
frameDelta = cv2.absdiff(firstFrame, gray)
thresh = cv2.threshold(frameDelta, 25, 255, cv2.THRESH_BINARY)[1]
thresh = cv2.dilate(thresh, None, iterations=2)
cnts = cv2.findContours(thresh.copy(), cv2.RETR_EXTERNAL,
cv2.CHAIN_APPROX_SIMPLE)
cnts = imutils.grab_contours(cnts)
for c in cnts:
if cv2.contourArea(c) < args["min_area"]:
continue
(x, y, w, h) = cv2.boundingRect(c)
cv2.rectangle(frame, (x, y), (x + w, y + h), (0, 255, 0), 2)
text = "Muon is fusing hydrogen"
cv2.putText(frame, "Room Status: {}".format(text), (10, 20),
cv2.FONT_HERSHEY_SIMPLEX, 0.5, (0, 0, 255), 2)
cv2.putText(frame, datetime.datetime.now().strftime("%A %d %B %Y %I:%M:%S%p"),
(10, frame.shape[0] - 10), cv2.FONT_HERSHEY_SIMPLEX, 0.35, (0, 0, 255), 1)
cv2.imshow("Security Feed", frame)
cv2.imshow("Thresh", thresh)
cv2.imshow("Frame Delta", frameDelta)
key = cv2.waitKey(1) & 0xFF
if key == ord("q"):
break
vs.stop() if args.get("video", None) is None else vs.release()
cv2.destroyAllWindows()
personally, I think that the problem is linux is having trouble using the internal camera, but I ofc have been wrong before, but if that is the problem, can somebody please help me fix it, and if it isn't, can somebody please help me out, and tell me what I need to fix please

How can i get side view of humans face on opencv by using the facial recognition?

I tried to use Haar cascades called haarcascade_profileface.xml and lbpcascade_profileface.xml together but the camera does not even open at all. How can I fix this issue where I want both haar cascades to work?
This is done on the raspberry pi and can also run on Linux and windows as well. Please explain as best as possible! Here is the code:
import numpy as np
import cv2
import time
import RPi.GPIO as GPIO
GPIO.setmode(GPIO.BCM)
GPIO.setwarnings(False)
GPIO.setup(18,GPIO.OUT)
face_cascade = cv2.CascadeClassifier('Haarcascade_profileface.xml')
side_face_cascade = cv2.CascadeClassifier('lbpcascade_frontalface_improved.xml')
prevTime = 0
## This will get our web camera
cap = cv2.VideoCapture(0)
font = cv2.FONT_HERSHEY_SIMPLEX
while True:
retval, frame = cap.read()
if not retval:
break
_, img = cap.read() ## This gets each frame from the video, cap.read returns 2 variables flag - indicate frame is correct and 2nd is f
##img = cv2.imread('Z.png') Then we get our image we want to use
gray = cv2.cvtColor(img, cv2.COLOR_BGR2GRAY) # This method only works on gray skin images, so we have to convert the gray scale to rgb image
faces = face_cascade.detectMultiScale(gray, 1.1, 5) ## Next, we detect the faces
if len(faces) > 0:
print("[INFO] found {0} faces!".format(len(faces)))
GPIO.output(18,GPIO.HIGH)
else:
print("No face")
GPIO.output(18,GPIO.LOW)
curTime = time.time()
sec = curTime - prevTime
prevTime = curTime
fps = 1/(sec)
str = "FPS : %0.1f" % fps
for (x, y, w, h) in faces: ## We draw a rectangle around the faces so we can see it correctly
cv2.rectangle(img, (x, y), (x+w, y+h), (255, 0, 0)) ## The faces will be a list of coordinates
cv2.putText(img, 'Myface', (x, y), font, fontScale=1, color=(255,70,120),thickness=2)
side_faces = side_face_cascade.detectMultiScale(gray, 1.1, 5)
for (ex, ey, ew, eh) in side_faces: ## We draw a rectangle around the faces so we can see it correctly
cv2.rectangle(img, (ex, ey), (ex+ew, ey+eh), (255, 0, 0)) ## The faces will be a list of coordinates
cv2.putText(img, 'Myface', (ex, ey), font, fontScale=1, color=(255,70,120),thickness=2)
cv2.putText(frame, 'Number of Faces Detected: ' + str, (0, 100), cv2.FONT_HERSHEY_SIMPLEX, 1, (0, 255, 0))
cv2.imshow('img', img) ## Last we show the image
x = cv2.waitKey(30) & 0xff
if x==27:
break
## Press escape to exit the program
cap.release()
OpenCV actually provides a "side-face" detector. It is called 'haarcascade_profileface.xml'. You can do:
side_face_cascade = cv2.CascadeClassifier('haarcascade_profileface.xml')
side_faces = side_face_cascade.detectMultiScale(gray, 1.1, 5)

Using RaspberryPi 3 with Pi-Camera and OpenCV to do PeopleCounting From the Top

I have been stuck for sometime now. Does anyone know of any links that can help me with using
(Hardware)- Raspberry Pi 3 connected to a Pi Cam NOT webcam
Then using both hardware mentioned above i wan to use any available software I'm guessing openCV to do people counting from the top.
Example Video: https://www.youtube.com/watch?v=BszUJXLR2oA
Almost all available examples using the raspberry pi to do people counting from the top doesnt use the picam.. webcams are big and bulky. So if there is any tutorial or what available please help. Thank You
==========================================================================
What i tried:
So the problem I'm having is i have a sample code that uses openCV with a webcam.. instructions can be found here :https://www.hackster.io/deligence-technologies/person-counting-system-using-opencv-and-python-faf14f
And in this code it uses a usb webcam thus the line that i commented that says "#HERE i need to use the pi cam instead" that line is using cv2.VideoCapture(0).. i need to know how to use the picam instead. any ideas?
import argparse
import datetime
import imutils
import math
import cv2
import numpy as np
width = 800
textIn = 0
textOut = 0
def testIntersectionIn(x, y):
res = -450 * x + 400 * y + 157500
if((res >= -550) and (res < 550)):
print (str(res))
return True
return False
def testIntersectionOut(x, y):
res = -450 * x + 400 * y + 180000
if ((res >= -550) and (res <= 550)):
print (str(res))
return True
return False
camera = cv2.VideoCapture(0) #HERE i need to use the pi cam instead
firstFrame = None
while True:
(grabbed, frame) = camera.read()
text = "Unoccupied"
if not grabbed:
break
frame = imutils.resize(frame, width=width)
gray = cv2.cvtColor(frame, cv2.COLOR_BGR2GRAY)
gray = cv2.GaussianBlur(gray, (21, 21), 0)
if firstFrame is None:
firstFrame = gray
continue
frameDelta = cv2.absdiff(firstFrame, gray)
thresh = cv2.threshold(frameDelta, 25, 255, cv2.THRESH_BINARY)[1]
thresh = cv2.dilate(thresh, None, iterations=2)
_, cnts, _ = cv2.findContours(thresh.copy(), cv2.RETR_EXTERNAL, cv2.CHAIN_APPROX_SIMPLE)
for c in cnts:
if cv2.contourArea(c) < 12000:
continue
(x, y, w, h) = cv2.boundingRect(c)
cv2.rectangle(frame, (x, y), (x + w, y + h), (0, 255, 0), 2)
cv2.line(frame, (width / 2, 0), (width, 450), (250, 0, 1), 2) #blue line
cv2.line(frame, (width / 2 - 50, 0), (width - 50, 450), (0, 0, 255), 2)#red line
rectagleCenterPont = ((x + x + w) /2, (y + y + h) /2)
cv2.circle(frame, rectagleCenterPont, 1, (0, 0, 255), 5)
if(testIntersectionIn((x + x + w) / 2, (y + y + h) / 2)):
textIn += 1
if(testIntersectionOut((x + x + w) / 2, (y + y + h) / 2)):
textOut += 1
if cv2.waitKey(1) & 0xFF == ord('q'):
break
cv2.putText(frame, "In: {}".format(str(textIn)), (10, 50),
cv2.FONT_HERSHEY_SIMPLEX, 0.5, (0, 0, 255), 2)
cv2.putText(frame, "Out: {}".format(str(textOut)), (10, 70),
cv2.FONT_HERSHEY_SIMPLEX, 0.5, (0, 0, 255), 2)
cv2.putText(frame, datetime.datetime.now().strftime("%A %d %B %Y %I:%M:%S%p"),
(10, frame.shape[0] - 10), cv2.FONT_HERSHEY_SIMPLEX, 0.35, (0, 0, 255), 1)
cv2.imshow("Security Feed", frame)
camera.release()
cv2.destroyAllWindows()
You say you read the link I posted in my comment but that is obviously not the case.
For clarity, this tutorial shows you how to do what you want to do and you need to read the code in the article and bring that into your code base.
What you are trying to do in your code is open the first USB webcam attached to your raspberry Pi. You do that on this line here:
camera = cv2.VideoCapture(0) #HERE i need to use the pi cam instead
As your comment does indeed state.
What you need to do is instead use the PiCamera library, like this:
# import the necessary packages
from picamera.array import PiRGBArray
from picamera import PiCamera
import time
import cv2
# initialize the camera and grab a reference to the raw camera capture
camera = PiCamera()
rawCapture = PiRGBArray(camera)
# allow the camera to warmup
time.sleep(0.1)
# grab an image from the camera
camera.capture(rawCapture, format="bgr")
image = rawCapture.array
# display the image on screen and wait for a keypress
cv2.imshow("Image", image)
cv2.waitKey(0)
The above example and tutorial should get you up and running with the basics, you can then modify the Hackster tutorial that you are following to use the PiCamera instead.

(Edited) BackgroundSubtractionMOG2 + Mean-Shift Tracking?

I am doing a project where its a motion based detection program.
However it detects changes in the background as "motion" so i'd like a way to recapture a new first frame every few minutes to replace the current one to fix this issue.
I am using a Raspberry Pi 2B and a Logitech Webcam.
The code i am using is based of : Pyimagesearch
This is my version of the code.
Please help me
(Edit)I have changed my code to a BackgroundSubtractionMOG2 now my issue is how do i add Mean-Shift Tracking so that it'll recognize its the same object that entered the screen in the frame earlier?
import sys
sys.path.append('/usr/local/lib/python3.4/site-packages')
import numpy as np
import cv2
import imutils
from imutils import contours
import datetime
import time
#cap = cv2.VideoCapture("/home/pi/Desktop/Proj/VideoTestSample.mp4")
cap = cv2.VideoCapture(0)
fgbg = cv2.createBackgroundSubtractorMOG2()
while (cap.isOpened()):
(grabbed, frame) = cap.read()
text = " "
if not grabbed:
break
frame = imutils.resize(frame, width=500)
gray = cv2.cvtColor(frame, cv2.COLOR_BGR2GRAY)
gray = cv2.medianBlur(gray, 5)
fgmask = fgbg.apply(gray)
thresh = cv2.erode(fgmask, None, iterations=2)
(_,cnts,hierarchy) = cv2.findContours(thresh.copy(), cv2.RETR_EXTERNAL, cv2.CHAIN_APPROX_SIMPLE)
for (i,c) in enumerate(cnts):
if cv2.contourArea(c) < 300:
continue
(x, y, w, h) = cv2.boundingRect(c)
cv2.rectangle(frame, (x,y), (x+w,y+h), (0,255,0), 2)
cv2.putText(frame, "#{}".format(i + 1), (x, y - 15),
cv2.FONT_HERSHEY_SIMPLEX, 0.45, (0, 0, 255), 2)
text = "REC"
cv2.putText(frame, "{}". format(text), (10,20),
cv2.FONT_HERSHEY_SIMPLEX, 0.5, (0, 0, 255), 2)
cv2.putText(frame, datetime.datetime.now().strftime("%A %d %B %Y %I:%M:%S%p"),
(10, frame.shape[0] - 10), cv2.FONT_HERSHEY_SIMPLEX, 0.35,(0,0,255), 1)
cv2.imshow('frame',frame)
cv2.imshow('gray', gray)
cv2.imshow('fgmask', fgmask)
if cv2.waitKey(1) & 0xFF == ord('q'):
break
cap.release()
cv2.destroyAllWindows()

Categories