Search code examples
pythonimage-processingface-recognition

Crop and save facial landmarks using PIL


I want to crop the top lip and bottom and save it into one image. I am using this GitHub library face_recognition

This is the code:

from PIL import Image, ImageDraw
import face_recognition

# Load the jpg file into a numpy array
image = face_recognition.load_image_file("me.jpg")

# Find all facial features in all the faces in the image
face_landmarks_list = face_recognition.face_landmarks(image)


for face_landmarks in face_landmarks_list:
    pil_image = Image.fromarray(image)
    d = ImageDraw.Draw(pil_image, 'RGBA')


    # Gloss the lips
    d.polygon(face_landmarks['top_lip'], fill=(150, 0, 0, 128), outline=None)
    d.polygon(face_landmarks['bottom_lip'], fill=(150, 0, 0, 128), outline=None)
    d.line(face_landmarks['top_lip'], fill=(150, 0, 0, 64), width=8)
    d.line(face_landmarks['bottom_lip'], fill=(150, 0, 0, 64), width=8)


    # cropped_top_lip = image.crop(face_landmarks['top_lip'])
    # cropped_top_lip.save('top_lip.jpg')
    # cropped_bottom_lip = image.crop(face_landmarks['bottom_lip'])
    # cropped_bottom_lip.save('bottom_lip.jpg')

    pil_image.save('me2.jpg')

This return the full image, I want only the lips part.

This is a print of face_landmarks['top_lip']:

[(498, 937), (546, 926), (597, 924), (637, 930), (676, 922), (726, 922), (772, 929), (756, 935), (677, 940), (637, 946), (597, 942), (516, 942)]


Solution

  • The .crop method takes a list containing 4 co-ordinates of the cropping area -

    [
        X_top_left_corner, Y_top_left_corner, 
        X_bottom_right_corner, Y_bottom_right_corner
    ] 
    

    What I can tell from the co-ordinates of face_landmarks['top_lip'], I think, these are the points on the outline of the lip. So, you need to find the left-top-most corner, and right-bottom-most corner, and pass that to crop method.

    [(498, 937), (546, 926), (597, 924), (637, 930), (676, 922), (726, 922), (772, 929), (756, 935), (677, 940), (637, 946), (597, 942), (516, 942)]

    To find the top-left corner, you'll need to find the least X co-ordinate and the least Y co-ordinate from this data. And these two co-ordinates can be present in separate tuples.

    For the bottom-right corner, you'll need the highest X co-ordinate and the highest y co-ordinate. Again, these two can be present in separate tuples.


    You can determine the corners like this:

    x1 = min(my_list)[0] # top left corner X
    y1 = min(my_list, key=lambda pair: pair[1])[1] # top left corner Y
    
    x2 = max(my_list)[0] # bottom right corner X
    y2 = max(my_list, key=lambda pair: pair[1])[1] # bottom right corner Y
    

    In this case the least X co-ordinate is 498 and the least Y co-ordinate is 922. These will make your top-left corner.

    The highest X co-ordinate is 772 and the highest Y co-ordinate is 946. These will be your bottom-right corner.

    This should work for cropping the top lip:

    # im.crop([x1, y1, x2, y2])
    im.crop([498, 922, 772, 946])