How to do face recognition in background in python? - python

Problem I am Facing
I am new to python and I want to do face recognition on the footage of webcam. So, I am using below code to achieve the task I want. I got it from youtube. My laptop has intel i7 7th gen, 4 core processor and 8 GB RAM, 4 GB graphics card (AMD R7 M445). When I start streaming I only get 20 fps even if my camera is capable of transmitting 30fps. On top of that when there is a person in frame, stream gets extremely unusable and it gets lower fps like 0.5 and even 0.2 fps.
My theory
So, I am guessing that this is a linear and pretty basic program. So, all the tasks including streaming and face recognition is being done on the same thread. That is why fps is getting delayed.
Solution I want
So, I want to create a structure where the face recognition will be done on a background thread and streaming will be displayed by on going foreground thread.
I still don't understand why my pc is displaying streaming at 20fps and not in 30fps.
Updated Code (Added perf_counter and added user defined functions)
Please refer to below link for old code.
Old py code
import cv2
from simple_facerec import SimpleFacerec
from datetime import datetime
from time import perf_counter
prev = 0
count = 0
vid_cod = ""
cap = ""
path =""
vid_name = ""
output = ""
sfr = ""
def vidCapture():
global cap,prev,count,sfr
while (cap.isOpened()):
ret, frame = cap.read()
# Detect Faces
startDetect = perf_counter()
face_locations, face_names = sfr.detect_known_faces(frame)
for face_loc, name in zip(face_locations, face_names):
y1, x2, y2, x1 = face_loc[0], face_loc[1], face_loc[2], face_loc[3]
#if the face is recognised then
if name != "Unknown" :
print("known Person detected :) => " + name)
stopDetect = perf_counter()
print(f"Time taken to identify face : {stopDetect-startDetect}")
#else if the face is unknown then
else :
stopDetect = perf_counter()
print(f"Time taken to identify face : {stopDetect-startDetect}")
print("Unknown Person detected :( => Frame captured...")
#show the frames on the display
cv2.imshow("Frame", frame)
#write frames to output object so it can save the video
output.write(frame)
#to terminate the process press "x"
if cv2.waitKey(1) & 0XFF == ord('x'):
break
cap.release()
output.release()
cv2.destroyAllWindows()
def __init__():
global prev,count,vid_cod,vid_name,cap,path,output,sfr
startSfr = perf_counter()
# Encode faces from a folder
sfr = SimpleFacerec()
sfr.load_encoding_images("images/")
stopSfr = perf_counter()
loadSfr = stopSfr-startSfr
print(f"Time taken to load sfr : {loadSfr}")
startCam = perf_counter()
# Load Camera
cap = cv2.VideoCapture('https://192.xxx.xxx.xxx:8080/videofeed')
#cap = cv2.VideoCapture(0)
stopCam = perf_counter()
loadCam = stopCam-startCam
print(f"Time taken to load cam : {loadCam}")
startCodec = perf_counter()
#Use MPEG video codec
vid_cod = cv2.VideoWriter_fourcc(*'MPEG')
stopCodec = perf_counter()
loadCodec = stopCodec-startCodec
print(f"Time taken to load codec : {loadCodec}")
startInit = perf_counter()
prev = 0
count = 0
#defining the path where video will be saved
path = 'C:/Users/Username/Documents/Recorded/'
#Encode video name with date and time
vid_name = str("recording_"+datetime.now().strftime("%b-%d-%Y_%H:%M").replace(":","_")+".avi")
#initialize video saving process as a "output" object
output = cv2.VideoWriter( str(path+vid_name) , vid_cod, 20.0 ,(640,480))
stopInit = perf_counter()
loadInit = stopInit - startInit
print(f"Time taken to load rest : {loadInit}")
vidCapture()
__init__()
This below file is simplefacerec which is imported in the above code.
simple_facerec.py
Note:
When I change fps in output from 20 to 25, saved video length gets decreased and when I change fps from 20 to 15 then video length gets increased. This is how I got the streaming fps of a video.
My Logs
After putting perf_counter() I found that face recognition alone is taking about 0.5 seconds. This whole code ran for about 7 to 8 seconds in which only 5 frames were displayed.
Time taken to load sfr : 2.3252885000001697
Time taken to load cam : 0.3193597999998019
Time taken to load codec : 1.3199998647905886e-05
Time taken to load rest : 0.0018557999974291306
known Person detected :) => Anirudhdhsinh Jadeja
Time taken to identify face : 0.4715902999996615
known Person detected :) => Anirudhdhsinh Jadeja
Time taken to identify face : 0.5326913999997487
known Person detected :) => Anirudhdhsinh Jadeja
Time taken to identify face : 0.4969602000019222
known Person detected :) => Anirudhdhsinh Jadeja
Time taken to identify face : 0.4868558000016492
known Person detected :) => Anirudhdhsinh Jadeja
Time taken to identify face : 0.4679383000002417

Related

using a python queue with an external class - not in the same file

I have a question regarding threading and queueing in python. I have a class that recognises faces and another script that is supposed to retrieve these recognised faces. And I'm unable to retrieve the recognised faces, once recognition starts
I posted another question that relates to this one, but here it's more about the actual user recognition part (this just in case someone stumbles over my other question and thinks this may be a duplicate).
So as said, I have a class that uses imutile and face_recognition to do just that - do face recognition. My issue with the class is that, once it's started it does recognise faces perfectly but it's not possible from an outside class (from within another script) to call any other method for example to retrieve a dict with the currently identified faces. I think this is problably because, once the actual recognition is called, the call to other methods within this class is not going through because of threading???. I attached the complete code for reference below.
Here is the code that is supposed to start the recogniser class and retrieve the results from within another script:
class recognizerAsync(Thread):
def __init__(self):
super(recognizerAsync,self).__init__()
print("initiating recognizer class from recognizerAsync")
if (use_user == True):
#myRecognizer = recognizer(consoleLog, debugMsg, run_from_flask)
self.myRecognizer = the_recognizer.recognizerAsync(False, True, True)
#face_thread = recognizerAsync(consoleLog, debugMsg, False)
def run(self):
print("starting up recogizer from callRecognizerThread")
self.myRecognizer.run()
if (use_user == True):
self.myRecognizer.start()
while (True):
if ( not q.full() ):
#fd = random.randint(1,10)
fd = self.myRecognizer.returnRecognizedFaces()
print(f"PRODUCER: putting into queue {fd}")
q.put(fd)
#else:
# print("ERROR :: Queue q is full")
time.sleep(10)
And I start this like so in the very end:
mirror = GUI(window)
mirror.setupGUI()
window.after(1000, mirror.updateNews)
face_thread = recognizerAsync()
face_thread.start()
window.mainloop()
My question is, how would I need to change either the recogniser classier the recognizerAsync class in the other script, so that while the method faceRecognizer() is running indefinitely, one can still call other methods - specifically returnRecognizedFaces()???
Thank you very much, folks.
#!/usr/bin/env python3
# import the necessary packages for face detection
from imutils.video import VideoStream
from imutils.video import FPS
import face_recognition
import imutils
import pickle
import time
import base64
import json
from threading import Thread
class recognizerAsync(Thread):
# How long in ms before a person detection is considered a new event
graceTimeBeforeNewRecognition = 6000 #60000
# the time in ms when a person is detected
timePersonDetected = 0
# ceate an empty faces dictionary to compare later with repetive encounterings
faces_detected_dict = {"nil": 0}
# Determine faces from encodings.pickle file model created from train_model.py
encodingsP = "" # "encodings.pickle"
# load the known faces and embeddings along with OpenCV's Haar
# cascade for face detection - i do this in the class initialization
#data = pickle.loads(open(encodingsP, "rb").read())
data = ''
# print dictionary of recognized faces on the console?
print_faces = False
debug_mnessagesa = False
called_from_flask = True # this changes the path to the
def __init__(self, print_val=False, debug_val=False, called_from_flask=True):
super(recognizerAsync,self).__init__()
self.print_faces = print_val
self.debug_messages = debug_val
if (called_from_flask == False):
encodingsP = "encodings.pickle"
else:
encodingsP = "Recognizer/encodings.pickle"
# load the known faces and embeddings along with OpenCV's Haar
# cascade for face detection
self.data = pickle.loads(open(encodingsP, "rb").read())
if (self.debug_messages == True):
print("Faces class initialized")
def run(self):
self.faceRecognizer()
def returnRecognizedFaces(self):
if (self.debug_messages == True):
print("from returnRecognizedFaces: returning: " + str((({k:self.faces_detected_dict[k] for k in self.faces_detected_dict if k!='nil'}))))
# print(f"from returnRecognizedFaces: returning: {self.faces_detected_dict}")
return(({k:self.faces_detected_dict[k] for k in self.faces_detected_dict if k!='nil'}))
def faceRecognizer(self):
try:
# initialize the video stream and allow the camera sensor to warm up
# Set the ser to the followng
# src = 0 : for the build in single web cam, could be your laptop webcam
# src = 2 : I had to set it to 2 inorder to use the USB webcam attached to my laptop
#vs = VideoStream(src=2,framerate=10).start()
vs = VideoStream(src=0,framerate=10).start()
#vs = VideoStream(usePiCamera=True).start()
time.sleep(2.0)
# start the FPS counter
fps = FPS().start()
if (self.debug_messages == True):
print("starting face detection - press Ctrl C to stop")
# loop over frames from the video file stream
while (True):
# grab the frame from the threaded video stream and resize it
# to 500px (to speedup processing)
frame = vs.read()
try:
frame = imutils.resize(frame, width=500)
except:
# Error: (h, w) = image.shape[:2]
# AttributeError: 'NoneType' object has no attribute 'shape'
break
# Detect the fce boxes
boxes = face_recognition.face_locations(frame)
# compute the facial embeddings for each face bounding box
encodings = face_recognition.face_encodings(frame, boxes)
names = []
# loop over the facial embeddings
for encoding in encodings:
# attempt to match each face in the input image to our known encodings
matches = face_recognition.compare_faces(self.data["encodings"], encoding)
name = "unknown" #if face is not recognized, then print Unknown
timePersonDetected = time.time()*1000.0
# check to see if we have found a match
if (True in matches):
# find the indexes of all matched faces then initialize a
# dictionary to count the total number of times each face
# was matched
matchedIdxs = [i for (i, b) in enumerate(matches) if b]
counts = {}
# loop over the matched indexes and maintain a count for
# each recognized face face
for i in matchedIdxs:
name = self.data["names"][i]
counts[name] = counts.get(name, 0) + 1
# determine the recognized face with the largest number
# of votes (note: in the event of an unlikely tie Python
# will select first entry in the dictionary)
name = max(counts, key=counts.get)
# If someone in your dataset is identified, print their name on the screen and provide them through rest
if (max(self.faces_detected_dict, key=self.faces_detected_dict.get) != name or timePersonDetected > self.faces_detected_dict[name] + self.graceTimeBeforeNewRecognition):
# put the face in the dictionary with time detected so we can prrovide this info
# in the rest endpoint for others - this is not really used internally,
# except for the timePersonDetected time comparison above
self.faces_detected_dict[name] = timePersonDetected
# update the list of names
names.append(name)
# exemplary way fo cleaning up the dict and removing the nil entry - kept here for reference:
#new_dict = ({k:self.faces_detected_dict[k] for k in self.faces_detected_dict if k!='nil'})
self.last_recognized_face = name
if (self.print_faces == True):
print(self.last_recognized_face)
# clean up the dictionary
new_dict = {}
for k, v in list(self.faces_detected_dict.items()):
if (v + self.graceTimeBeforeNewRecognition) < (time.time()*1000.0) and str(k) != 'nil':
if (self.debug_messages == True):
print('entry ' + str(k) + " dropped due to age")
else:
new_dict[k] = v
self.faces_detected_dict = new_dict
if (self.debug_messages == True):
print(f"faces dict: {self.faces_detected_dict}")
# update the FPS counter
fps.update()
time.sleep(1)
except KeyboardInterrupt:
if (self.debug_messages == True):
print("Ctrl-C received - cleaning up and exiting")
pass

Is there a way to save raw opencv images to video?

I'm pretty new to opencv and I've been trying to build an automatic stat tracker for a game I'm playing. I want to collect videos to test the object recognition and stat tracking code, and thought I might as well use the capture code I've already written to make the test videos. So I've been using the pywin32 api to make images for opencv to process, and thought I'd just directly use those images to save to video.
The problem I'm having is that the videos being made are invalid and useless.
So now that I've gone through the background I can move on to the actual questions.
Is there a specific format the opencv images need to be in before they can be saved? or is there a way to possibly feed the images through the VideoCapture class in a non clunky way (like recapturing the cv.imshow window)? I don't want to directly use the VideoCapture class, because it requires the specified window to be always on top. I might also just be doing it wrong.
Code included below.
class VideoScreenCapture:
frame_time_limit = 0
time_elapsed = 0
frame_width = None
frame_height = None
frame_size = None
fps = None
window_capture = None
filming = False
output_video = None
scaling_factor = 2/3
def __init__(self, window_capture=None, fps=5):
"""
window_capture: a custom window capture object that is associated with the window to be captured
fps: the frames per second of the resulting video
"""
self.fps = fps
# calculate the amount of time per frame (inverse of fps)
self.frame_time_limit = 1/self.fps
self.window_capture = window_capture
# get the size (in pixels) of the window getting captured
self.frame_width, self.frame_height, self.frame_size = self.window_capture.get_frame_dimentions()
# initialize video writer
self.output_video = cv.VideoWriter(
f'assets{sep}Videos{sep}test.avi',
cv.VideoWriter_fourcc(*'XVID'),
self.fps,
self.frame_size)
def update(self, dt):
"""dt: delta time -> the time since the last update"""
# only updates if the camera is on
if self.filming:
# update the elapsed time
self.time_elapsed += dt
# if the elapsed time is more than the time segment calculated for the given fps
if self.time_elapsed >= self.frame_time_limit:
self.time_elapsed -= self.frame_time_limit
# window capture takes screenshot of the capture window and resizes it to the wanted side
frame = self.window_capture.get_screenshot()
frame = cv.resize(
frame,
dsize=(0, 0),
fx=self.scaling_factor,
fy=self.scaling_factor)
# the image is then saved to video
self.output_video.write(frame)
# show the image in a separate window
cv.imshow("Computer Vision", frame)
"""methods for stopping and starting the video capture"""
def start(self):
self.filming = True
def stop(self):
self.filming = False
"""releases video writer: use when done"""
def release_vw(self):
self.output_video.release()
This while loop that runs the code
vsc.start()
loop_time = time()
while cv.waitKey(1) != ord('q'):
vsc.update(time() - loop_time)
loop_time = time()

Communication with Thorlabs uc480 camera

I was able to get the current image from a Thorlabs uc480 camera using instrumental. My issue is when I try to adjust the parameters for grab_image. I can change cx and left to any value and get an image. But cy and top only works if cy=600 and top=300. The purpose is to create a GUI so that the user can select values for these parameters to zoom in/out an image.
Here is my code
import instrumental
from instrumental.drivers.cameras import uc480
from matplotlib.figure import Figure
import matplotlib.pyplot as plt
paramsets = instrumental.list_instruments()
cammer = instrumental.instrument(paramsets[0])
plt.figure()
framer= cammer.grab_image(timeout='1s',copy=True,n_frames=1,exposure_time='5ms',cx=640,
left=10,cy=600,top=300)
plt.pcolormesh(framer)
The above code does not give an image if I choose cy=600 and top=10. Are there any particular value set to be used for these parameters? How can I get an image of the full sensor size?
Thorlabs has a Python programming interface available as a download on their website. It is very well documented, and can be installed locally via pip.
Link:
https://www.thorlabs.com/software_pages/ViewSoftwarePage.cfm?Code=ThorCam
Here is an example of a simple capture algorithm that might help get you started:
from thorlabs_tsi_sdk.tl_camera import TLCameraSDK
from thorlabs_tsi_sdk.tl_mono_to_color_processor import MonoToColorProcessorSDK
from thorlabs_tsi_sdk.tl_camera_enums import SENSOR_TYPE
# open the TLCameraSDK dll
with TLCameraSDK() as sdk:
cameras = sdk.discover_available_cameras()
if len(cameras) == 0:
print("Error: no cameras detected!")
with sdk.open_camera(cameras[0]) as camera:
#camera.disarm() # ensure any previous session is closed
# setup the camera for continuous acquisition
camera.frames_per_trigger_zero_for_unlimited = 0
camera.image_poll_timeout_ms = 2000 # 2 second timeout
camera.arm(2)
# need to save the image width and height for color processing
image_width = camera.image_width_pixels
image_height = camera.image_height_pixels
# initialize a mono to color processor if this is a color camera
is_color_camera = (camera.camera_sensor_type == SENSOR_TYPE.BAYER)
mono_to_color_sdk = None
mono_to_color_processor = None
if is_color_camera:
mono_to_color_sdk = MonoToColorProcessorSDK()
mono_to_color_processor = mono_to_color_sdk.create_mono_to_color_processor(
camera.camera_sensor_type,
camera.color_filter_array_phase,
camera.get_color_correction_matrix(),
camera.get_default_white_balance_matrix(),
camera.bit_depth
)
# begin acquisition
camera.issue_software_trigger()
# get the next frame
frame = camera.get_pending_frame_or_null()
# initialize frame attempts and max limit
frame_attempts = 0
max_attempts = 10
# if frame is null, try to get a frame until
# successful or until max_attempts is reached
if frame is None:
while frame is None:
frame = camera.get_pending_frame_or_null()
frame_attempts += 1
if frame_attempts == max_attempts:
raise TimeoutError("Timeout was reached while polling for a frame, program will now exit")
image_data = frame.image_buffer
if is_color_camera:
# transform the raw image data into RGB color data
color_data = mono_to_color_processor.transform_to_24(image_data, image_width, image_height)
save_data = np.reshape(color_data,(image_height, image_width,3))
camera.disarm()
You can also process the image after capture with the PIL library.

close the frame after detecting qr code with opencv

I've been working on this project to detect qr-code and make attendance and have the outputs in sql database and so far I've gotten everything to work out,
The Problem is I actually want the frame to close when a single QR code is detected, decoded and all of the above process is completed also The camera works and the frame shows up but it is very slow when detecting the QR code.
What actually happens is after the frame is open it constantly and after it detects a QR code, it actually logs multiple (around 15) instances of a QR being detected after like 2 seconds. Also the frame is still up and I can still detect Images.
The frame closes, only after pressing the waitkey which is 27 or 'Esc'
So I am actually looking for 2 Things:
How to close the frame after detecting a QR Code?
(additional question) how do I detect only one qr code (whether the frame closes or not). So when I scan the QR code, either the frame closes and I am left with one decoded data OR I scan the QR code and the frame remains open, until I hit 'Esc' and I am left with one decoded data.
here is the full code for reference:
import cv2
import os
import csv
import sqlite3
import vobject
from datetime import datetime
import numpy as np
from pyzbar import pyzbar
path = 'qr-codes'
images = []
classNames = []
lists = os.listdir(path)
for cl in lists:
curImage = cv2.imread(f'{path}/{cl}')
images.append(curImage)
classNames.append(os.path.splitext(cl)[0])
def main():
camera = cv2.VideoCapture(0)
ret, frame = camera.read()
while ret:
ret, frame = camera.read()
recognizer = parse_vcard(frame)
win_name = 'Face and QR Detection'
cv2.imshow(win_name, recognizer)
if cv2.waitKey(1) & 0xFF == 27:
break
camera.release()
cv2.destroyAllWindows()
def parse_vcard(frame):
barcodes = pyzbar.decode(frame)
for barcode in barcodes:
x, y, w, h = barcode.rect
cv2.rectangle(frame, (x, y),(x+w, y+h), (0, 255, 0), 2)
mydata = barcode.data.decode('utf-8')
vcard = vobject.readOne(mydata)
make_attendence(vcard)
return frame
def make_attendence(vcard):
name = str(vcard.contents['n'][0].value)
# print(type(Employee.name))
profession = str(vcard.contents['title'][0].value)
now = datetime.now()
date = datetime.date(now)
attendance_time = now.strftime('%H:%M:%S')
leave_time = '21:00:00'
connection = sqlite3.connect('employee.db')
conn = connection.cursor()
# conn.execute("""CREATE TABLE employees (
# name text,
# profission text,
# date date,
# attendance_time time,
# leave_time time
# )""")
conn.execute("INSERT INTO employees VALUES \
(:name, :profession, :date, :attendance_time, :leave_time)", {
'name': name,
'profession': profession,
'date': date,
'attendance_time': attendance_time,
'leave_time': leave_time})
connection.commit()
connection.close()
if __name__ == '__main__':
main()
do not reconnect to the database for every QR code and every picture you take.
connect once. keep the connection.
the imshow window is a window. a "frame" is commonly the term for one picture/image from a video. you can't "close" a frame.
you can close imshow windows with destroyAllWindows or individual imshow windows using destroyWindow
use python's "pdb" debugger. use a profiler to investigate the time cost of your code. use time.perf_counter() and do the measurements yourself, if you don't find a profiler you like.

Accessing Beaglebone python opencv usb camera but the display showing black screen

I am facing issue while accessing USB camera using beagle-bone black wireless.
Firstly the error is "select timeout" exception which was resolved by this post
Now I am facing the black screen in output.
Here is the testing code I am using.
from cv2 import *
# initialize the camera
cam = VideoCapture(0) # 0 -> index of camera
print "Cam capture"
cam.set(3,320)
cam.set(4,240)
print "Cam set"
s, img = cam.read()
print "Cam read"
if s: # frame captured without any errors
namedWindow("cam-test",CV_WINDOW_AUTOSIZE)
imshow("cam-test",img)
while True:
key = waitKey(30)
if key == ord('q') :
destroyWindow("cam-test")
I have already check that video0 in /dev directory.
The issue is that you need to call 'cam.read()andimshow()` inside the while loop
What you're doing is that you're reading just the first frame, then showing it, and you whileloop isn't doing anything. When the camera boots, the first frame is just a blank screen , which is what you see.
The code should be more like:
while True:
s, img = cam.read()
imshow("cam-test",img)
key = waitKey(30)
if key == ord('q') :
destroyWindow("cam-test")

Categories