[PYTHON] Don't you want to say that you made a face recognition program?

Face recognition in the times

Shenzhen is one of the most IT-developed cities in China. According to what I hear, you can make payments by simply holding your face over from convenience store payments to vending machines. Whether it's good or bad, it's amazing. It's futuristic and cool. Then make me too! I made it last year with a glue like w, but I couldn't make it into an article, so I decided to make it this time. The systems and programs introduced this time are the first works that touched Python properly, and the first memorable works that touched machine learning.

Overview

After taking an image of your face using the in-camera of your PC, processing and padding the image using the library OpenCV, you can learn the face with traincascade and create a program to identify the individual. The language used was Python3.

Program flow

This time, we created two types of face image processing, learning program and face recognition program.

Face image processing, learning program flow

① Start the PC camera with video capture (2) Save 40 face-recognized frames as images. ③ Add 40 images in various forms to increase the learning data. ④ Generate learning commands ⑤ Type the generated command into the console to start learning. ⑥ Generate a cascade file after learning is completed

Face recognition program flow

① Read the registrant's cascade file (2) Pass each frame through a cascade file to identify individuals.

Folder structure

Face image processing, learning program

face_learner/
         ├─face_learner.py
         ├─haarcascade_frontalface_default.xml
         ├─neg.txt
         ├─pos.txt
         ├─cascade/
         ├─neg/
             ├─neg1.jpg
             ├─neg2.jpg
             ├─neg3.jpg
              …
             └─neg265.jpg
         ├─pic/
             ├─pic1.jpg
             ├─pic2.jpg
             ├─pic3.jpg
              …
             └─pic1639.jpg
         └─pos.vec

This folder structure is only the files around the learning program. The personal identification program is not included.

Program description (learning program)

Please refer to the commented out explanation of what to do easily in the source code. .. Two types of correct image (positive image) and incorrect image (negative image) are required for learning. The correct image is the face image you want to learn, and you also need the coordinate information of where the face is in the image. The incorrect image can be anything other than the correct image. This time, 265 cute animal images are prepared and stored in the neg directory in advance. スクリーンショット 2019-11-19 18.35.33.png

First of all, we will start by collecting these correct images. For the correct answer image, first take 40 face images and then increase the number to about 1640 to improve the learning accuracy. The more the number of correct images, the higher the learning accuracy, so it is advisable to increase the number as appropriate. I feel that there is a consultation with the PC specifications. Since you will be learning a new face, you need to delete all the unnecessary data you used before. You should be able to handle it smartly here too (laughs). Collecting facial images is the main purpose of this program. The face is recognized using the pre-loaded cascading file for face recognition, and 40 face images are first taken. Start adding water from here. Water is added by adding various additions to the image. It seems that there is already a tool in this part, so it is absolutely quick to use it. This time, I also made my own programming practice.

[Type of image modification] • Flip horizontal • Saturation change • Contrast change • Change brightness • Sharpness change • ± 15 degree rotation

There is a lot of room for improvement in the processing here. First of all, it is absolutely good to make each processing part functional. In this program, the number of photos is counted and added each time, so it is better to apply it a little smarter. After collecting the images, it is necessary to create a text file that summarizes the information of the correct image. This is the path of the image file, which part of which image has the face, and the coordinate information of the four quadrilaterals that surround the part with the face. The format is [Number of face faces Coordinates (x) Coordinates (y) Coordinates (w) Coordinates (h)]. w is the width and h is the height. An example is shown below.

pos.txt


pic/pic1389.jpg 1 454 328 547 547
pic/pic302.jpg 1 565 85 380 380
pic/pic464.jpg 1 947 389 31 31
pic/pic470.jpg 1 663 238 341 341
pic/pic1438.jpg 1 658 341 540 540
pic/pic316.jpg 1 620 88 376 376
pic/pic1376.jpg 1 795 414 376 376
pic/pic1410.jpg 1 476 408 379 379
pic/pic1404.jpg 1 497 394 383 383
pic/pic1362.jpg 1 754 400 372 372
pic/pic869.jpg 1 625 235 420 420
pic/pic855.jpg 1 380 185 381 381
pic/pic666.jpg 1 361 241 424 424
pic/pic100.jpg 1 312 91 360 360
pic/pic64.jpg 1 622 101 371 371
pic/pic114.jpg 1 279 84 375 375
pic/pic70.jpg 1 618 89 382 382
pic/pic672.jpg 1 1213 216 43 43
pic/pic672.jpg 1 369 274 385 385
pic/pic882.jpg 1 721 313 353 353
.
.
.

Once you've generated the text, all you have to do is generate the command. First, a command to convert to a vector file using the information of the correct image. The next command to learn. Python has the ability to execute Linux commands from a python program, such as the os.system () and subprocess.call () functions, but I didn't get it so I can do it manually. I am trying to generate a command to. If anyone can tell me about this part, I will study! When the learning is finished, the cascade.xml file is created in the cascade directory, so read it and perform personal identification. It will be easier to understand if the file name is an individual name as appropriate.

Program description (discrimination program)

Here, individual identification is performed using the cascade file created earlier. The only library loaded by this program is OpenCV (cv2). Pass the frame captured by the camera through the cascade file each time, and when the face is raised, enclose the closed part with a frame. First, when a person's face is recognized, it is surrounded by a white frame, and when a person is recognized, it is surrounded by a blue frame, and the name is drawn on it.

S__24559619.jpg

With this kind of feeling, you can identify individuals in real time with an in-camera!

Actual program

I'm really reluctant to expose dirty code, but I'm going to publish the whole code, believing it will help. Please note that it is long enough to die.

Image collection + learning program

face_leaner.py


import os
import re
import numpy as np
import time
import glob
import shutil
import PIL.Image
from PIL import ImageEnhance
import subprocess
import cv2

#uer_name = input("Who are you")
cascade = cv2.CascadeClassifier('haarcascade_frontalface_default.xml')

#----------------------------------------------
#mov, pic,Clear pos

os.chdir('cascade')
for x in glob.glob('*.xml'):
    os.remove(x)

os.chdir('../')
os.chdir('pic')
for x in glob.glob('*.jpg'):
    os.remove(x)

os.chdir('../')
os.chdir('pos')
for x in glob.glob('*.jpg'):
    os.remove(x)

os.chdir('../')
os.remove('pos.txt')
#----------------------------------------------
#pos.Create txt
f = open('pos.txt', 'a')

#----------------------------------------------
#Collect facial images
cap = cv2.VideoCapture(0)
cascade_path1 = "haarcascade_frontalface_default.xml"
cascade_path2 = 'lbpcascade_profileface.xml'
cascade1 = cv2.CascadeClassifier(cascade_path1)
cascade2 = cv2.CascadeClassifier(cascade_path2)
color = (255,255,255)
picture_num = 1
while True:
    ret, frame = cap.read()
    facerect1 = cascade1.detectMultiScale(frame, scaleFactor=1.7, minNeighbors=4, minSize=(100,100))
    font = cv2.FONT_HERSHEY_SIMPLEX
    cv2.putText(frame, str(picture_num), (10,500), font, 4,(0,0,0),2,cv2.LINE_AA)
    if len(facerect1) > 0:
        for (x,y,w,h) in facerect1:
            #cv2.rectangle(frame,(x,y),(x+w,y+h),(255,0,0),2)
            picture_name = 'pic/pic' + str(picture_num) + '.jpg'
            cv2.imwrite(picture_name, frame)
            #text = picture_name + ' 1 ' + str(x) + ' ' + str(y) + ' ' + str(w) + ' ' + str(h) + '\n'
            #f.write(text)
            picture_num = picture_num + 1
    cv2.imshow("frame", frame)
    if picture_num == 41:
        break
    if cv2.waitKey(1) & 0xFF == ord('q'):
        break
cap.release()
cv2.destroyAllWindows()

#----------------------------------------------
#Start padding
#Count the number of pictures in pictures
dir = os.getcwd()
dirPic = dir + "/pic"
files = os.listdir(dirPic)
count = 0
for file in files:
    count = count + 1
os.chdir('pic')
#Number of photos
imageNum = count
#Flip horizontal
for num in range(1, count+1):
    name = 'pic' + str(num) + '.jpg'
    if os.path.exists(name) :
        pass
    else :
        continue
    if os.path.getsize(name) == 0:
        os.remove(name)
        continue
    img = cv2.imread(name)
    yAxis = cv2.flip(img, 1)
    newName = 'pic' + str(imageNum) + '.jpg'
    cv2.imwrite(newName,yAxis)
    imageNum = imageNum + 1
print('OK')
print('NEXT STAGE')

#Count the number of photos
dir = os.getcwd()
files = os.listdir(dir)
count = 0
for file in files:
    count = count + 1
print(count)

#Number of photos
imageNum = count
#Fix imageNum
picNum = imageNum


SATURATION = 0.5
CONTRAST = 0.5
BRIGHTNESS = 0.5
SHARPNESS = 2.0
#Change the saturation
for num in range(1, count+1):
    name = 'pic' + str(num) + '.jpg'
    if os.path.exists(name) :
        pass
    else :
        print('NO')
        continue
    if os.path.getsize(name) == 0:
        os.remove(name)
        continue
    img = PIL.Image.open(name)
    saturation_converter = ImageEnhance.Color(img)
    saturation_img = saturation_converter.enhance(SATURATION)
    newName = 'pic' + str(imageNum) + '.jpg'
    saturation_img.save(newName)
    imageNum = imageNum + 1
print('OK')
print('NEXT STAGE')

#Count the number of photos
dir = os.getcwd()
files = os.listdir(dir)
count = 0
for file in files:
    count = count + 1
print(count)

#Number of photos
imageNum = count

count = picNum
#Change the contrast
for num in range(1, count+1):
    name = 'pic' + str(num) + '.jpg'
    if os.path.exists(name) :
        pass
    else :
        print('NO')
        continue
    if os.path.getsize(name) == 0:
        os.remove(name)
        continue
    img = PIL.Image.open(name)
    contrast_converter = ImageEnhance.Contrast(img)
    contrast_img = contrast_converter.enhance(CONTRAST)
    newName = 'pic' + str(imageNum) + '.jpg'
    contrast_img.save(newName)
    imageNum = imageNum + 1
print('OK')
print('NEXT STAGE')

#Count the number of photos
dir = os.getcwd()
files = os.listdir(dir)
count = 0
for file in files:
    count = count + 1
print(count)

#Number of photos
imageNum = count

count = picNum
#Change the brightness
for num in range(1, count+1):
    name = 'pic' + str(num) + '.jpg'
    if os.path.exists(name) :
        pass
    else :
        print('NO')
        continue
    if os.path.getsize(name) == 0:
        os.remove(name)
        continue
    img = PIL.Image.open(name)
    brightness_converter = ImageEnhance.Brightness(img)
    brightness_img = brightness_converter.enhance(BRIGHTNESS)
    newName = 'pic' + str(imageNum) + '.jpg'
    brightness_img.save(newName)
    imageNum = imageNum + 1
print('OK')
print('NEXT STAGE')

#Count the number of photos
dir = os.getcwd()
files = os.listdir(dir)
count = 0
for file in files:
    count = count + 1
print(count)

#Number of photos
imageNum = count

count = picNum
#Change sharpness
for num in range(1, count+1):
    name = 'pic' + str(num) + '.jpg'
    if os.path.exists(name) :
        pass
    else :
        print('NO')
        continue
    if os.path.getsize(name) == 0:
        os.remove(name)
        continue
    img = PIL.Image.open(name)
    sharpness_converter = ImageEnhance.Sharpness(img)
    sharpness_img = sharpness_converter.enhance(SHARPNESS)
    newName = 'pic' + str(imageNum) + '.jpg'
    sharpness_img.save(newName)
    imageNum = imageNum + 1
print('OK')
print('NEXT STAGE')

#Count the number of photos
dir = os.getcwd()
files = os.listdir(dir)
count = 0
for file in files:
    count = count + 1
print(count)

#Number of photos
imageNum = count
#Fix imageNum
picNum = imageNum

#15 degree rotation
for num in range(1, count+1):
    name = 'pic' + str(num) + '.jpg'
    if os.path.exists(name) :
        pass
    else :
        print('NO')
        continue
    if os.path.getsize(name) == 0:
        os.remove(name)
        continue
    #Image loading
    img = cv2.imread(name)
    h, w = img.shape[:2]
    size = (w, h)

    #Specifying the rotation angle
    angle = 15
    angle_rad = angle/180.0*np.pi

    #Calculate image size after rotation
    w_rot = int(np.round(h*np.absolute(np.sin(angle_rad))+w*np.absolute(np.cos(angle_rad))))
    h_rot = int(np.round(h*np.absolute(np.cos(angle_rad))+w*np.absolute(np.sin(angle_rad))))
    size_rot = (w_rot, h_rot)

    #Rotate around the center of the original image
    center = (w/2, h/2)
    scale = 1.0
    rotation_matrix = cv2.getRotationMatrix2D(center, angle, scale)

    #Add translation(rotation + translation)
    affine_matrix = rotation_matrix.copy()
    affine_matrix[0][2] = affine_matrix[0][2] -w/2 + w_rot/2
    affine_matrix[1][2] = affine_matrix[1][2] -h/2 + h_rot/2

    img_rot = cv2.warpAffine(img, affine_matrix, size_rot, flags=cv2.INTER_CUBIC)

    cv2.imwrite(newName, img_rot)
    newName = 'pic' + str(imageNum) + '.jpg'
    saturation_img.save(newName)
    imageNum = imageNum + 1
print('OK')
print('NEXT STAGE')

#Count the number of photos
dir = os.getcwd()
files = os.listdir(dir)
count = 0
for file in files:
    count = count + 1
print(count)

#Number of photos
imageNum = count

#-15 degree rotation
for num in range(1, count+1):
    name = 'pic' + str(num) + '.jpg'
    if os.path.exists(name) :
        pass
    else :
        print('NO')
        continue
    if os.path.getsize(name) == 0:
        os.remove(name)
        continue
    #Image loading
    img = cv2.imread(name)
    h, w = img.shape[:2]
    size = (w, h)

    #Specifying the rotation angle
    angle = -15
    angle_rad = angle/180.0*np.pi

    #Calculate image size after rotation
    w_rot = int(np.round(h*np.absolute(np.sin(angle_rad))+w*np.absolute(np.cos(angle_rad))))
    h_rot = int(np.round(h*np.absolute(np.cos(angle_rad))+w*np.absolute(np.sin(angle_rad))))
    size_rot = (w_rot, h_rot)

    #Rotate around the center of the original image
    center = (w/2, h/2)
    scale = 1.0
    rotation_matrix = cv2.getRotationMatrix2D(center, angle, scale)

    #Add translation(rotation + translation)
    affine_matrix = rotation_matrix.copy()
    affine_matrix[0][2] = affine_matrix[0][2] -w/2 + w_rot/2
    affine_matrix[1][2] = affine_matrix[1][2] -h/2 + h_rot/2

    img_rot = cv2.warpAffine(img, affine_matrix, size_rot, flags=cv2.INTER_CUBIC)

    cv2.imwrite(newName, img_rot)
    newName = 'pic' + str(imageNum) + '.jpg'
    saturation_img.save(newName)
    imageNum = imageNum + 1
print('OK')
print('NEXT STAGE')

#Count the number of photos
dir = os.getcwd()
files = os.listdir(dir)
count = 0
for file in files:
    count = count + 1
print(count)
print('OK')
print('COMPLETE')

#------------------------------------------------------
#Text file creation
#cascade = cv2.CascadeClassifier('haarcascade_frontalface_default.xml')
#os.chdir('pic')
for num in glob.glob('*.jpg'):
    img = cv2.imread(num)
    gray = cv2.cvtColor(img, cv2.COLOR_BGR2GRAY)
    faces = cascade.detectMultiScale(gray)
    for (x,y,w,h) in faces:
        text = 'pic/' + num + ' 1 ' + str(x) + ' ' + str(y) + ' ' + str(w) + ' ' + str(h) + '\n'
        f.write(text)

cmd = 'opencv_createsamples -info pos.txt -vec pos.vec -num ' + str(count)
print(cmd)

cmd = 'opencv_traincascade -data ./cascade -vec pos.vec -bg neg.txt -numPos 1500 numNeg 255'
print(cmd)

print('COMPLETE.')

Personal identification program

face_id.py


# coding: utf-8

import cv2

font = cv2.FONT_HERSHEY_SIMPLEX

if __name__ == "__main__":
    cap = cv2.VideoCapture(0)
    cascade_path_human = 'haarcascade_frontalface_default.xml'
    cascade_path_hirosugu = "cascade_hirosugu.xml"
    cascade_path_kenta = 'cascade_kenta.xml'

    cascade_hirosugu = cv2.CascadeClassifier(cascade_path_hirosugu)
    cascade_human = cv2.CascadeClassifier(cascade_path_human)
    cascade_kenta = cv2.CascadeClassifier(cascade_path_kenta)

    color = (255,0,0)

    while True:
        ret, frame = cap.read()

        facerect_human = cascade_human.detectMultiScale(frame, scaleFactor=1.7, minNeighbors=4, minSize=(100,100))
        facerect_hirosugu = cascade_hirosugu.detectMultiScale(frame, scaleFactor=1.7, minNeighbors=4, minSize=(100,100))
        facerect_kenta = cascade_kenta.detectMultiScale(frame, scaleFactor=1.7, minNeighbors=4, minSize=(100,100))

        if len(facerect_human) > 0:
            for rect in facerect_human:
                cv2.rectangle(frame, tuple(rect[0:2]), tuple(rect[0:2]+rect[2:4]), (255,255,255), thickness=2)


        if len(facerect_hirosugu) > 0:
            for rect in facerect_hirosugu:
                cv2.rectangle(frame, tuple(rect[0:2]), tuple(rect[0:2]+rect[2:4]), color, thickness=2)
                cv2.putText(frame, 'Hirosugu Takeshita', tuple(rect[0:2]), font, 2,(0,0,0),2,cv2.LINE_AA)


        if len(facerect_kenta) > 0:
            for rect in facerect_kenta:
                cv2.rectangle(frame, tuple(rect[0:2]), tuple(rect[0:2]+rect[2:4]), color, thickness=2)
                cv2.putText(frame, 'Kenta Suzuki', tuple(rect[0:2]), font, 2,(0,0,0),2,cv2.LINE_AA)

        cv2.imshow("frame", frame)

        #Press the q key to end the loop
        if cv2.waitKey(1) & 0xFF == ord('q'):
            break

    cap.release()
    cv2.destroyAllWindows()

By the way, it is recommended to put the generated cascade file (.xml learning file) in the same hierarchy as the face recognition program!

Finally

Right? Isn't it easy? This is a garbage program for professionals! You might think that, but you can call yourself an Ese ML engineer. If it is true, the accuracy will be further improved if you do the feature extraction and write the NN yourself (I don't know because I'm not so familiar with it).

Also, there are many articles about OpenCV, so please refer to that as well. Since this article is only a fairly thin trace, it is recommended that you thoroughly investigate the detailed mechanism and other commands during learning.

Well, this time it's a personal identification by machine learning, so I asked them to bring back only the knowledge that it would be easier to collect face images in this way, and I will make a more accurate face ID and introduce it. I would be grateful if you could!

Thank you for reading for a long time!

Recommended Posts

Don't you want to say that you made a face recognition program?
I made a program to notify you by LINE when switches arrive
If you want to create a Word Cloud.
I don't want to take a coding test
Do you want me to fix that copy?
I want to exe and distribute a program that resizes images Python3 + pyinstaller
Don't ask "Are you sure you want to continue connecting"
When you want to play a game via Proxy
I made a system that allows you to tweet just by making a phone call
When you want to plt.save in a for statement
I want to say that there is data preprocessing ~
I made a program to collect images in tweets that I liked on twitter with Python
A program that failed when trying to create a linebot with reference to "Dialogue system made with python"
If you don't understand mathematical symbols, you can write a program.
[Django] A memorandum when you want to communicate asynchronously [Python3]
I made a fucking app that won't let you skip
When you want to hit a UNIX command on Python
I made you to execute a command from a web browser
I made a script to say hello at my Koshien
[Subprocess] When you want to execute another Python program in Python code
I made a program to solve (hint) Saizeriya's spot the difference
If you want to become a data scientist, start with Kaggle
Don't write Python if you want to speed it up with Python
I want to use a wildcard that I want to shell with Python remove
I made a program that solves the spot the difference in seconds
Qiskit: I want to create a circuit that creates arbitrary states! !!
If you want to assign csv export to a variable in python
Try to write a program that abuses the program and sends 100 emails
When you want to sort a multidimensional list by multiple lines
I made a library that adds docstring to a Python stub file.
Created a service that allows you to search J League data
I made a program that automatically calculates the zodiac with tkinter
[Python] A program that rotates the contents of the list to the left
A note that you want to manually decorate the parameters passed in the Django template form item by item
[AtCoder for beginners] A story about the amount of calculation that you want to know very roughly
Key operations you want to know
[Python] I made a decorator that doesn't seem to have any use.
I made a web application in Python that converts Markdown to HTML
[Python] A program that calculates the number of socks to be paired
A memo that allows you to change Pineapple's Python environment with pyenv
When you want to replace a column with a missing value (NaN) column by column
I made a Dir en gray face classifier using TensorFlow --⑥ Learning program
[Python] If you want to draw a scatter plot of multiple clusters
A program that automatically corrects "Takenoko no Sato" to "Kinoko no Yama" "correctly"
I want to identify the alert email. --Is that x a wildcard? ---
Allow Slack to notify you of the end of a time-consuming program process
A shell script to make sure you don't forget the pipenv shell again
I made a program to input what I ate and display calories and sugar
I made a library konoha that switches the tokenizer to a nice feeling
Q. Do you want to do something like generics that takes value from []?
I made a program to check the size of a file in Python
[Python3] Code that can be used when you want to cut out an image in a specific size
If you want to make a Windows application (exe) that can be actually used now using only Python