I am using the below code for cropping faces from images in a directory

import cv2
import glob
import os

def facechop(image,imgname):
    facedata = "haarcascade_frontalface_default.xml"
    cascade = cv2.CascadeClassifier(facedata)

    img = cv2.imread(image)

    minisize = (img.shape[1],img.shape[0])
    miniframe = cv2.resize(img, minisize)

    faces = cascade.detectMultiScale(miniframe)

    for f in faces:
        x, y, w, h = [ v for v in f ]
        cv2.rectangle(img, (x,y), (x+w,y+h), (255,255,255))

        sub_face = img[y:y+h, x:x+w]
        face_file_name = "faces/" + imgname + "_" + str(y) + ".jpg"
        cv2.imwrite(face_file_name, sub_face)

    # cv2.imshow(image, img)
    # cv2.waitKey(0)


# if __name__  '__main__':
path = "images"
images = glob.glob(path + "/*")
for imgpath in images[0:]:
    imagename = os.path.basename(imgpath)
    imgname = os.path.splitext(imagename)[0]
    facechop(imgpath, imgname)

# while(True):
#     key = cv2.waitKey(20)
#     if key in [27, ord('Q'), ord('q')]: 
#         break

The code is too slow and sometimes even hangs the system. Please help improve.

  • \$\begingroup\$ Do you really still need cv2.destroyAllWindows() after commenting the 2 lines above? \$\endgroup\$ – 301_Moved_Permanently Mar 2 '17 at 20:33
  • \$\begingroup\$ I have tried commenting that too. It does not help. \$\endgroup\$ – Pankaj Jain Mar 3 '17 at 0:11

So there are a couple of improvements that can be made to your code,

The first one would be, you are creating a new CascadeClassifier object for every picture this is a costly operation.

The next is the fact that you are resizing the image, to the size of the image? I am unsure why you are doing that, depending on the size of the faces in your images you could resize them to be smaller and the search for faces in the picture.

Harr cascade detection is costly you can customize the parameters depending on the sizes of faces in your image.

Your code draws a box around every face and then isolates it, why is that? Lots of this code looks to be vestiges of old code. Removing old code can help make it more clear what is going on.

You number the face images based on the y value of the box; it would be much cleaner to simply report the index of the face (e.g. face 0, 1, 2, 3, etc...)

Based on this, here is some straightforward revised code that is much cleaner, but detectMultiScale is still ~99% of the running time

import os

import cv2

image_path = "images"

def save_faces(cascade, imgname):
    img = cv2.imread(os.path.join(image_path, imgname))
    for i, face in enumerate(cascade.detectMultiScale(img)):
        x, y, w, h = face
        sub_face = img[y:y + h, x:x + w]
        cv2.imwrite(os.path.join("faces", "{}_{}.jpg".format(imgname, i)), sub_face)

if __name__ == '__main__':
    face_cascade = "haarcascade_frontalface_default.xml"
    cascade = cv2.CascadeClassifier(face_cascade)
    # Iterate through files
    for f in [f for f in os.listdir(image_path) if os.path.isfile(os.path.join(image_path, f))]:
        save_faces(cascade, f)

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.