pythonopencv

Cropping with Python and OpenCV to get passport ratio photos


I got the following python code that uses OpenCV to take a given picture and then look for a face and crop that face and save it with the word "Cropped" appended to the file name. Although it's working great, there's two things I cannot seem to figure out how to do.

  1. It seems to be cropping very close to the face edges, I'd like to set an offset to convert it so that it fetches a passport like photo from the crop
  2. It seems to also add a red border.

Can anyone kindly advise how I can possibly solve these two issues?

Thanks

import cv2 

img = cv2.imread('testImage.jpg') 
gray = cv2.cvtColor(img, cv2.COLOR_BGR2GRAY)
face_cascade = cv2.CascadeClassifier('haarcascade_frontalface_alt.xml') 
faces = face_cascade.detectMultiScale(gray, 1.1, 4)

for (x, y, w, h) in faces: 
   cv2.rectangle(img, (x, y), (x+w, y+h),  
                 (0, 0, 255), 2) 
     
   faces = img[y:y + h, x:x + w] 
   cv2.imwrite('testImage-Cropped.jpg', faces) 

cv2.waitKey()

Solution

    1. You need to remove the cv2.rectangle line, this is what adds the red rectangle around the face
    2. Add some margin when cropping the faces, limit to valid indices, play around with the value of "margin" to find the one you like
    3. cv2.waitKey() just hangs the execution, not sure why you need it
    4. Note that you're overwriting the same file each time, i've added a counter so each face is saved in a different image file

    The full example:

    import cv2 
    
    img = cv2.imread('testImage.jpg') 
    gray = cv2.cvtColor(img, cv2.COLOR_BGR2GRAY)
    face_cascade = cv2.CascadeClassifier('haarcascade_frontalface_alt.xml') 
    faces = face_cascade.detectMultiScale(gray, 1.1, 4)
    margin = 20
    for i, (x, y, w, h) in enumerate(faces):
       a = max(y - margin//2, 0)
       b = min(y + h + margin//2, img.shape[0])
       c = max(x - margin//2, 0)
       d = min(x + w + margin//2, img.shape[1])
       faces = img[a:b,  c:d] 
       cv2.imwrite(f'testImage-Cropped_{i}.jpg', faces) 
    

    Edit: Per request I've added an example

    original image: “The Most Intelligent Photo Ever Taken”: The 1927 Solvay Council Conference, Featuring Einstein, Bohr, Curie, Heisenberg, Schrödinger & More source

    collage of detected faces: all detected faces

    full code:

    import cv2 
    import numpy as np
    
    img = cv2.imread('solvay-bw.jpeg') 
    gray = cv2.cvtColor(img, cv2.COLOR_BGR2GRAY)
    face_cascade = 
    cv2.CascadeClassifier('haarcascade_frontalface_alt.xml') 
    faces = face_cascade.detectMultiScale(gray, 1.1, 4)
    margin = 20
    imface = []
    w_max, h_max = 0, 0
    for i, (x, y, w, h) in enumerate(faces):
        a = max(y - margin//2, 0)
        b = min(y + h + margin//2, img.shape[0])
        c = max(x - margin//2, 0)
        d = min(x + w + margin//2, img.shape[1])
        face = img[a:b,  c:d]
        imface.append(face)
        w_max = max(b-a, w_max)
        h_max = max(d-c, h_max)
        cv2.imwrite(f'testImage-Cropped_{i}.jpg', face)
    
    imdraw = np.zeros((h_max*4, int(w_max*np.ceil(len(faces)/4)), 3),     dtype=np.uint8)
    imdraw[..., :] = (242, 248, 248)
    for i, face in enumerate(imface):
        x = w_max*(i//4)
        y = h_max*(i%4)
        imdraw[y:y+face.shape[1], x:x+face.shape[0]] = face
    cv2.imwrite(f'testImage-AllFaces.jpg', imdraw)