[Python] Created a transformation app for world champion "Mr. Satan"

Introduction

Everyone's longing, world champion "Mr. Satan". I made an app that can be transformed into that "Mr. Satan". If you wear the characteristic eyebrows, beard and afro hair, you are also "Mr. Satan"!

** Miscellaneous ** (w)!

Technically, we use a library called face_recognition to get the coordinates of various parts of the face, and based on that, we calculate the position and size of the eyebrows, beard, and afro and draw them.

What is face_recognition?

Face Recognition is the world's simplest face recognition library that can recognize and manipulate faces.

Face Recognition

Recognize and manipulate faces from Python or from the command line with the world's simplest face recognition library.

To use it, first install it with pip.

$ pip install opencv-python
$ pip install opencv-contrib-python
$ pip install cmake
$ pip install face_recognition

As a test, try to get the face parts using the image below.

import face_recognition
import cv2

F = "image.jpg "

image = face_recognition.load_image_file(F)
face_landmarks_list = face_recognition.face_landmarks(image)

print(face_landmarks_list)

Img15790044321.jpg

I got the coordinates of each part. Let's plot the obtained coordinates on the actual image.

bgr = cv2.imread(F)
for face_landmarks in face_landmarks_list:

    for facial_feature in face_landmarks.keys():
        for i in range(len(face_landmarks[facial_feature])):
            cv2.drawMarker(bgr, face_landmarks[facial_feature][i], 
                           color=(255, 0, 0), markerType=cv2.MARKER_CROSS, thickness=1)

cv2.imshow('', bgr)
cv2.waitKey(0)
cv2.destroyAllWindows()

You can see that it is recognized correctly.

Actual code

1. Install with pip

$ pip install opencv-python
$ pip install opencv-contrib-python
$ pip install cmake
$ pip install face_recognition

2. Library import / constant declaration

import face_recognition
import cv2
from PIL import Image
import numpy as np
#Maximum vertical and horizontal width of the image
MAX_IMAGE_WIDTH = 10000
MAX_IMAGE_HEIGHT = 10000

3. Function definition (overlay drawing / coordinates are stored in an array)

class CvOverlayImage(object):
    """
    [summary]
Overlay the specified image on the OpenCV format image
    """

    def __init__(self):
        pass

    @classmethod
    def overlay(
            cls,
            cv_background_image,
            cv_overlay_image,
            point,
    ):
        """
        [summary]
Overlay the specified image on the OpenCV format image
        Parameters
        ----------
        cv_background_image : [OpenCV Image]
        cv_overlay_image : [OpenCV Image]
        point : [(x, y)]
        Returns : [OpenCV Image]
        """
        overlay_height, overlay_width = cv_overlay_image.shape[:2]

        #Convert OpenCV format images to PIL format(Including α value)
        #background image
        cv_rgb_bg_image = cv2.cvtColor(cv_background_image, cv2.COLOR_BGR2RGB)
        pil_rgb_bg_image = Image.fromarray(cv_rgb_bg_image)
        pil_rgba_bg_image = pil_rgb_bg_image.convert('RGBA')
        #Overlay image
        cv_rgb_ol_image = cv2.cvtColor(cv_overlay_image, cv2.COLOR_BGRA2RGBA)
        pil_rgb_ol_image = Image.fromarray(cv_rgb_ol_image)
        pil_rgba_ol_image = pil_rgb_ol_image.convert('RGBA')

        # composite()Since images of the same size are required, prepare images for compositing
        pil_rgba_bg_temp = Image.new('RGBA', pil_rgba_bg_image.size,
                                     (255, 255, 255, 0))
        #Specify coordinates and superimpose
        pil_rgba_bg_temp.paste(pil_rgba_ol_image, point, pil_rgba_ol_image)
        result_image = \
            Image.alpha_composite(pil_rgba_bg_image, pil_rgba_bg_temp)

        #Convert to OpenCV format image
        cv_bgr_result_image = cv2.cvtColor(
            np.asarray(result_image), cv2.COLOR_RGBA2BGRA)

        return cv_bgr_result_image
def GetPosi(posi_name):
    """
    [summary]
Get the coordinates of the specified face part
    Parameters
    ----------
    posi_name : [str]
    Returns : [left_X、right_X, Top_Y, Bottom_Y]
    """

    for face_landmarks in face_landmarks_list:

        minX = MAX_IMAGE_WIDTH 
        maxX = 0
        minY = MAX_IMAGE_HEIGHT
        maxY = 0

        for i in range(len(face_landmarks[posi_name])):

            if face_landmarks[posi_name][i][0] < minX:
                minX  = face_landmarks[posi_name][i][0]

            if face_landmarks[posi_name][i][0] > maxX:
                maxX  = face_landmarks[posi_name][i][0]

            if face_landmarks[posi_name][i][1] < minY:
                minY = face_landmarks[posi_name][i][1]

            if face_landmarks[posi_name][i][1] > maxY:
                maxY = face_landmarks[posi_name][i][1]

    return [minX, maxX, minY, maxY]

4. Get face landmarks

#File name definition
F = "sample.jpg "
#Get face landmarks from images
image_fl = face_recognition.load_image_file(F)
face_landmarks_list = face_recognition.face_landmarks(image_fl)

5. Calculate the position of eyebrows, beard, and afro

#Get the coordinates of each part
eye_r = GetPosi('right_eye')
eye_l = GetPosi('left_eye')
mouse = GetPosi('top_lip')
nose  = GetPosi('nose_tip')
chin  = GetPosi('chin')

#Calculate eyebrow width and whiskers width from the acquired coordinates
brow_h   = int((eye_r[3] - eye_r[2]) * 2) 
face_w   = chin[1] - chin[0]
beard_h  = int((mouse[2] - nose[2]) * 0.7)
beard_w  = int((face_w - (mouse[1] - mouse[0])) * 0.2)
beard_h2 = int((chin[3] - mouse[2]) * 0.6)
beard_h3 = int((chin[3] - mouse[2]) * 0.3)
scale    = int(face_w / 20)
scale2   = scale * 2

#Calculate the location of the afro image(There is fine adjustment because it is not symmetrical)
hair_w   = int(face_w * 1.83)
hair_h   = int(hair_w * 0.64)
hair_x   = int(chin[0] - (hair_w / 2 - face_w / 2) + scale * 1.5)
hair_y   = eye_l[2] - hair_h
#Eyebrow / beard coordinate calculation
eyeb_r = np.array(
    [
        [eye_r[0] - scale2, eye_r[2] - brow_h], 
        [eye_r[1] + scale2, eye_r[2] - brow_h - scale2], 
        [eye_r[1] + scale2, eye_r[2] - scale * 2] , 
        [eye_r[0] - scale2, eye_r[2]]
    ]
)

eyeb_l = np.array(
    [
        [eye_l[0] - scale2, eye_l[2] - brow_h - scale2], 
        [eye_l[1] + scale2, eye_l[2] - brow_h], 
        [eye_l[1] + scale2, eye_l[2]] , 
        [eye_l[0] - scale2, eye_l[2] - scale * 2]
    ]
)

beard_c = np.array(
    [
        [mouse[0] - scale, mouse[2] - beard_h], 
        [mouse[1] + scale, mouse[2] - beard_h], 
        [mouse[1] + scale, mouse[2] -  0] , 
        [mouse[0] - scale, mouse[2] -  0]
    ]
)

beard_l = np.array(
    [
        [mouse[0] - beard_w, mouse[2] - beard_h - scale], 
        [mouse[0] - 5, mouse[2] - beard_h], 
        [mouse[0] - 5, mouse[2] + beard_h2], 
        [mouse[0] - beard_w, mouse[2] + beard_h3]
    ]
)

beard_r = np.array(
    [
        [mouse[1] + 5, mouse[2] - beard_h], 
        [mouse[1] + beard_w, mouse[2] - beard_h - scale], 
        [mouse[1] + beard_w, mouse[2] + beard_h3], 
        [mouse[1] + 5, mouse[2] + beard_h2]
    ]
)

6. Drawing eyebrows, beard, and afro

#Read files with OpenCV
image = cv2.imread(F)

#Eyebrows / beard / drawing process
cv2.fillConvexPoly(image, points =eyeb_r, color=(0, 0, 0))
cv2.fillConvexPoly(image, points =eyeb_l, color=(0, 0, 0))
cv2.fillConvexPoly(image, points =beard_c, color=(0, 0, 0))
cv2.fillConvexPoly(image, points =beard_l, color=(0, 0, 0))
cv2.fillConvexPoly(image, points =beard_r, color=(0, 0, 0))
#Overlay display of Afro image
cv_background_image = image
cv_overlay_image = cv2.imread(
    "head.png ",
    cv2.IMREAD_UNCHANGED)  # IMREAD_Specify UNCHANGED and read with α

cv_overlay_image = cv2.resize(cv_overlay_image, (hair_w, hair_h))
point = (hair_x, hair_y)

image = CvOverlayImage.overlay(cv_background_image, cv_overlay_image, point)

#Image drawing
cv2.imshow("image", image)
cv2.waitKey(0)
cv2.destroyAllWindows()

bonus

This is the Afro image used. The background is transparent. Please set it as head.png and put it in the working folder.

head.png

At the end

At the moment, only the front image is supported. Face Recognition gets the coordinates with a nice feeling, so I made it with a little processing. Some of the output was interesting personally, but I gave up because of the portrait right. I hope you can play with various images.

Reference site

** [OpenCV] [Python] Draw an image with transparency on top of the image **

Recommended Posts

[Python] Created a transformation app for world champion "Mr. Satan"
Make Qt for Python app a desktop app
Created a Python wrapper for the Qiita API
Qt for Python app self-update
Created a library for python that can easily handle morpheme division
From building a Python environment for inexperienced people to Hello world
[For beginners] How to register a library created in Python in PyPI
[python] I created a follow-up correlation diagram for twitter (Gremlin edition)
Created AtCoder test tool for Python
Python #Hello World for super beginners
I created a template for a Python project that can be used universally
Create a simple GUI app in Python
Let's create a virtual environment for Python
Daemonize a Python web app with Supervisor
[Mac] Building a virtual environment for Python
Created a darts trip with python (news)
Launch a Flask app in Python Anywhere
Get a token for conoha in python
A tool for easily entering Python code
A typed world that begins with Python
Make a desktop app with Python with Electron
I created a password tool in Python.
Building a Python development environment for AI development
A textbook for beginners made by Python beginners