Search code examples
pythonmultithreadingopencvface-recognition

multi-threaded face detection opencv


I have a script for single-threaded sequential face detection in a photo, and a script for cutting out faces. How do I convert to multithreading? So that the images are not processed sequentially, but simultaneously, parallel to each other.

import os
import cv2
import numpy as np

# Define paths
base_dir = os.path.dirname(__file__)
prototxt_path = os.path.join(base_dir + 'data/deploy.prototxt')
caffemodel_path = os.path.join(base_dir + 'data/weights.caffemodel')

# Read the model
model = cv2.dnn.readNetFromCaffe(prototxt_path, caffemodel_path)

# Create directory 'updated_images' if it does not exist
if not os.path.exists('updated_images'):
print("New directory created")
os.makedirs('updated_images')

# Loop through all images and save images with marked faces
for file in os.listdir(base_dir + 'images'):
file_name, file_extension = os.path.splitext(file)
if (file_extension in ['.png','.jpg']):
print("Image path: {}".format(base_dir + 'images/' + file))

image = cv2.imread(base_dir + 'images/' + file)

(h, w) = image.shape[:2]
blob = cv2.dnn.blobFromImage(cv2.resize(image, (300, 300)), 1.0, (300, 300), (104.0, 177.0, 123.0))

model.setInput(blob)
detections = model.forward()

# Create frame around face
for i in range(0, detections.shape[2]):
box = detections[0, 0, i, 3:7] * np.array([w, h, w, h])
(startX, startY, endX, endY) = box.astype("int")

confidence = detections[0, 0, i, 2]

# If confidence > 0.5, show box around face
if (confidence > 0.5):
cv2.rectangle(image, (startX, startY), (endX, endY), (255, 255, 255), 2)

cv2.imwrite(base_dir + 'updated_images/' + file, image)
print("Image " + file + " converted successfully")

I tried to push the face detection and selection into def and then monitor the parallel streams through pool and map, but I am very weak in this, and obviously did something wrong. The script just stopped working.


Solution

  • Here is how I would do it:

    import os
    import cv2
    import numpy as np
    import threading
    
    base_dir = os.path.dirname(__file__)
    prototxt_path = os.path.join(base_dir + 'data/deploy.prototxt')
    caffemodel_path = os.path.join(base_dir + 'data/weights.caffemodel')
    
    model = cv2.dnn.readNetFromCaffe(prototxt_path, caffemodel_path)
    
    if not os.path.exists('updated_images'):
        print("New directory created")
        os.makedirs('updated_images')
    
    def process(file, base_dir):
        print("Image path: {}".format(base_dir + 'images/' + file))
        image = cv2.imread(base_dir + 'images/' + file)
        blob = cv2.dnn.blobFromImage(cv2.resize(image, (300, 300)), 1.0, (300, 300), (104.0, 177.0, 123.0))
        model.setInput(blob)
        detections = model.forward()
        h, w = image.shape[:2]
        for i in range(detections.shape[2]):
            box = detections[0, 0, i, 3:7] * np.array([w, h, w, h])
            startX, startY, endX, endY = box.astype("int")
            confidence = detections[0, 0, i, 2]
            if confidence > 0.5:
                cv2.rectangle(image, (startX, startY), (endX, endY), (255, 255, 255), 2)
        cv2.imwrite(base_dir + 'updated_images/' + file, image)
        print("Image " + file + " converted successfully")
    
    for file in os.listdir(base_dir + 'images'):
        file_name, file_extension = os.path.splitext(file)
        if file_extension in ['.png','.jpg']:
            thread = threading.Thread(target=process, args=(file, base_dir))
            thread.start()
    

    Most of it is the same as your code, except a large chunk is now in a function. I also took the liberty of removing some redundant code, such as how you don't need parenthesis to unpack an iterable, nor do you need parenthesis to do if statements.

    As I don't have the files you open in your code, I'm unable to test it out, hence if there are any problems, there might be something I missed, so feel free to ping me if that happens.