Course notes: 60 fps face detection

Preamble

Notes from Latest 60 Fps FACE Detection | OpenCV Python | Computer Vision

See also

Courses

Required Files

  • Videos – 6 videos (can use web cam – but this has frame rate limit) – from pexels.com

Required packages

  • opencv-python
  • mediapipe

Course notes

New project FaceDetectionProject

New file FaceDetectionBasics.py

Template video

# Template_video.py

import cv2
import time

camW, camH = 640, 480

cap = cv2.VideoCapture('Videos/2.mp4')
cap.set(3, camW)
cap.set(4, camH)


while True:

    success, image = cap.read()

    cv2.imshow('Image', image)
    cv2.waitKey(1)

Template video with FPS

# Template_video_fps.py

import cv2
import time

camW, camH = 640, 480

cap = cv2.VideoCapture('Videos/2.mp4')
cap.set(3, camW)
cap.set(4, camH)

pTime = 0

while True:

    success, image = cap.read()

    cTime = time.time()
    fps = 1/(cTime-pTime)
    pTime = cTime

    cv2.putText(image, f'FPS: {int(fps)}', (20, 70), cv2.FONT_HERSHEY_PLAIN, 3, (0, 0, 255), 2)

    cv2.imshow('Image', image)
    cv2.waitKey(1)

Next step

8:04

drawingUtils. We can draw ourselves. 6 points for eyes, nose and ears.

Add

mpFaceDetection = mp.solutions.face_detection
mpDraw = mp.solutions.drawing_utils
faceDetection = mpFaceDetection.FaceDetection()

 

Convert image to RGB and get the results

imageRGB = cv2.cvtColor(image, cv2.COLOR_BGR2RGB)
results = faceDetection.process(imageRGB)
print(results)

Frame rate reduces from 160 to 60 – 90, due to processing. A class is returned. To extract the info

 

extract the info

11:21

if results.detections:
    # Iterate through the faces
    for id, detection in enumerate(results.detection):
        print(id, detection)
        print(detection.score)
        print(detection.location_data.relative_bounding_box)

Normalised values

15:13

the x and y coords are normalised (0 -> 1), so must be multiplied by width and height of the image

 

We can draw using the provided utilities

mpDraw.drawDetection(detection)

The landmark points can be draw quite erratically, you need video zoomed in on the face. However, we are going to focus on the bounding box and the score.

We want to draw the box ourselves, using the coords details.

# This is very long
print(detection.location_data.relative_bounding_box.xmin)

We need to shorten the reference.

bboxC = detection.location_data.relative_bounding_box

Now convert (but we need the width and height)

iH, iW, iC = image.shape

Use short variable names (i.e. iW) when the scope is small. If the variable to to be used elsewhere, then use more descriptive names, imageWidth.

bbox = int(bboxC.xmin * iW), int(bboxC.ymin * iH), \
       int(bboxC.width * iW), int(bboxC.height * iH)

Now we draw our own box, without using the built-in utils…

cv2.rectangle(image, bbox, (255, 0, 255), 2)

Increase the confidence

21:45

To reduce the false boxes, increase the confidence

# faceDetection = mpFaceDetection.FaceDetection()
faceDetection = mpFaceDetection.FaceDetection(0.75)

Displaying the confidence level

22:25

Put [0] to remove the [] around the score displayed.

Make a module

24:37

To reduce the code in the script, make a module, FaceDetectionModule.py

Make the main conditional

 

if __name__ == '__main__':
    nmain()

make the main(). Add the video loop and fps

def main():
    camW, camH = 640, 480

    cap = cv2.VideoCapture('Videos/2.mp4')
    cap.set(3, camW)
    cap.set(4, camH)

    pTime = 0

    while True:
        success, image = cap.read()

        cTime = time.time()
        fps = 1 / (cTime - pTime)
        pTime = cTime

        cv2.putText(image, f'FPS: {int(fps)}', (20, 70), cv2.FONT_HERSHEY_PLAIN, 3, (0, 0, 255), 2)

        cv2.imshow('Image', image)
        cv2.waitKey(1)

Make the __init__(), add the self., put in the declarations

def __init(self, minDetectionCon=0.5):
    self.minDetectionCon = minDetectionCon
    self.mpFaceDetection = mp.solutions.face_detection
    self.mpDraw = mp.solutions.drawing_utils
    # self.faceDetection = self.mpFaceDetection.FaceDetection()
    self.faceDetection = self.mpFaceDetection.FaceDetection(self.minDetectionCon)

Add a method

def findFaces(self, image, draw=True):

Add the rest of the code

def findFaces(self, image, draw=True):

    imageRGB = cv2.cvtColor(image, cv2.COLOR_BGR2RGB)
    self.results = self.faceDetection.process(imageRGB)
    print(self.results)

    if self.results.detections:
        # Iterate through the faces
        for faceId, detection in enumerate(self.results.detection):
            print(faceId, detection)
            print(detection.score)
            print(detection.location_data.relative_bounding_box)

            # This is very long
            print(detection.location_data.relative_bounding_box.xmin)

            bboxC = detection.location_data.relative_bounding_box

            iH, iW, iC = image.shape
            bbox = int(bboxC.xmin * iW), int(bboxC.ymin * iH), \
                   int(bboxC.width * iW), int(bboxC.height * iH)

            # mpDraw.drawDetection(detection)
            cv2.rectangle(image, bbox, (255, 0, 255), 2)

            cv2.putText(image, f'Score: {int(detection.score[0]*100)} %', (bbox[0], bbox[1] - 20), cv2.FONT_HERSHEY_PLAIN, 2, (255, 0, 255), 2)

Self.results is not really required (it could stay as results), but maybe we want to use it later

Remember to remove (or comment) any print statements.

Return the faces

29:00

Return, bbox, id and the score (landmarks too is optional). Create a list, bboxes[], for one face.

bboxes = []

Add at the end of the for loop

bboxes.append([bbox, detection.score])
# bboxes.append([id, bbox, detection.score])  # Not required, use the index of the list

Add the return

return image, bboxes

 

 

 

Testing/using the module

30:46

Add in main()

detector = FaceDetector()

add after image captured

image, bboxes = detector.findFaces(image)

print if you want

print(bboxes)

Add a target

35:25

def fancyDraw(self, image, bbox, length=30, thickness=7, rect_thickness=1, colour=(255, 0, 255)):
    x, y, w, h = bbox
    x1, y1 = x + w, y + h  # bottomRight
    cv2.rectangle(image, bbox, colour, rect_thickness)
    # Top Left x, y
    cv2.line(image, (x, y), (x + length, y), colour, thickness)
    cv2.line(image, (x, y), (x, y + length), colour, thickness)
    # Top Right x1, y
    cv2.line(image, (x1, y), (x1 - length, y), colour, thickness)
    cv2.line(image, (x1, y), (x1, y + length), colour, thickness)
    # Bottom Left x, y1
    cv2.line(image, (x, y1), (x + length, y1), colour, thickness)
    cv2.line(image, (x, y1), (x, y1 - length), colour, thickness)
    # Top Left x1, y1
    cv2.line(image, (x1, y1), (x1 - length, y1), colour, thickness)
    cv2.line(image, (x1, y1), (x1, y1 - length), colour, thickness)
    return image

 

This is the end, my friend

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out /  Change )

Google photo

You are commenting using your Google account. Log Out /  Change )

Twitter picture

You are commenting using your Twitter account. Log Out /  Change )

Facebook photo

You are commenting using your Facebook account. Log Out /  Change )

Connecting to %s