I'm stuck with a problem with my functions. I'm building a face recognition system which should work like this:
- Read a video file
- Use opencv and haarcascade to recognize the frames from the video where are human faces detected
- Save those frames locally`
To this point, everything goes fine, here's the function I use to do these steps:
import cv2
from imutils.video import FPS
import numpy as np
import argparse
import imutils
import os
async def take_snapshots(file):
cascPath = "haarcascade_frontalface_default.xml"
faceCascade = cv2.CascadeClassifier(cascPath)
# construct the argument parse and parse the arguments
# ap = argparse.ArgumentParser()
# ap.add_argument("-v", "--video", required=True,
# help="video.mp4")
# args = vars(ap.parse_args())
# open a pointer to the video stream and start the FPS timer
# stream = cv2.VideoCapture(args["video"])
stream = cv2.VideoCapture(file)
fps = FPS().start()
try:
# creating a folder named data
if not os.path.exists('data'):
os.makedirs('data')
# if not created then raise error
except OSError:
print('Error: Creating directory of data')
# frame
currentframe = 0
# loop over frames from the video file stream
while True:
# grab the frame from the threaded video file stream
(grabbed, frame) = stream.read()
# if the frame was not grabbed, then we have reached the end
# of the stream
if not grabbed:
break
# resize the frame and convert it to grayscale (while still
# retaining 3 channels)
frame = imutils.resize(frame, width=980)
frame = cv2.cvtColor(frame, cv2.COLOR_BGR2GRAY)
frame = np.dstack([frame, frame, frame])
faces = faceCascade.detectMultiScale(
frame,
scaleFactor=1.1,
minNeighbors=4,
minSize=(20, 20),
maxSize=(40, 40),
# flags=cv2.CASCADE_SCALE_IMAGE
)
for (x, y, w, h) in faces:
# cv2.rectangle(frame, (x, y), (x+w, y+h), (255, 0, 0), 3)
if grabbed:
# Save just the rectangle faces in SubRecFaces
# sub_face = frame[y:y+h, x:x+w]
name = './data/frame' + str(currentframe) + '.jpg'
print('Creating...' + name)
# writing the extracted images
cv2.imwrite(name, frame)
currentframe += 1
if cv2.waitKey(1) & 0xFF == ord('q'):
break
else:
break
# show the frame and update the FPS counter
# cv2.imshow("Frame", frame)
# cv2.waitKey(1)
fps.update()
# stop the timer and display FPS information
fps.stop()
print("[INFO] elasped time: {:.2f}".format(fps.elapsed()))
print("[INFO] approx. FPS: {:.2f}".format(fps.fps()))
# do a bit of cleanup
stream.release()
cv2.destroyAllWindows()
- Take a saved frame and use aws rekognition api to detect faces from it (if you want to know why, it's because of accuracy)
- Save a new copy from the frames that include faces, with bounding boxes
Step 4 goes well, no problems there. when the code reaches step 5, I encounter a problem where my code goes through every frame saved at step 3, it also skips the two first frames where are no faces. After that it saves frame2.jpg as pic0.jpg, as it should. Problem is, after that it should save a new picture (pic1, pic2 etc) from every frame but instead it just overwrites the pic0.jpg with every round. I know the problem is with my loop, I just can't figure out how to fix it.
Here are the functions I use for steps 4 and 5:
import boto3
from pathlib import Path
import os
import cv2
import io
from PIL import Image, ImageDraw, ExifTags, ImageColor
async def detect_faces(photo):
image = Image.open(open(photo, 'rb'))
stream = io.BytesIO()
image.save(stream, format=image.format)
image_binary = stream.getvalue()
client = boto3.client('rekognition')
response = client.detect_faces(
Image={'Bytes': image_binary}, Attributes=['ALL'])
draw = ImageDraw.Draw(image)
print('Detected faces in ' + photo)
currentpic = 0
for face in response['FaceDetails']:
print('Confidence: ' + str(face['Confidence']))
box = face['BoundingBox']
imgWidth, imgHeight = image.size
left = imgWidth * box['Left']
top = imgHeight * box['Top']
width = imgWidth * box['Width']
height = imgHeight * box['Height']
# print('Left: ' + '{0:.0f}'.format(left))
# print('Top: ' + '{0:.0f}'.format(top))
# print('Face Width: ' + "{0:.0f}".format(width))
# print('Face Height: ' + "{0:.0f}".format(height))
points = (
(left, top),
(left + width, top),
(left + width, top + height),
(left, top + height),
(left, top)
)
draw.line(points, fill='#00d400', width=2)
name = './results/pic' + str(currentpic) + '.jpg'
print('Creating final pic.....' + name)
image.save(name)
currentpic += 1
return len(response['FaceDetails'])
async def main():
directory_in_str = './data'
pathlist = Path(directory_in_str).glob('**/*.jpg')
try:
if not os.path.exists('results'):
os.makedirs('results')
# if not created then raise error
except OSError:
print('Error: Creating directory of data')
for path in pathlist:
# path is object not string
path_in_str = str(path)
# print(path_in_str)
photo = path_in_str
face_count = await detect_faces(photo)
print("Faces detected: " + str(face_count))
Finally, here is the main function I use to run these functions:
import read_frames_slow
import detect_faces
import asyncio
async def main():
await read_frames_slow.take_snapshots('video.mp4')
await detect_faces.main()
loop = asyncio.get_event_loop()
loop.run_until_complete(main())
loop.close()
Would really appreciate a help to solve this problem.