Face Recognition

人臉辨識順序:

偵測人臉--->臉部裁切-->資料訓練--->人臉辨識

Face Detection with Open CV

import os

import cv2

import numpy as np


# 載入人臉追蹤模型

detector = cv2.CascadeClassifier('haarcascade_frontalface_default.xml')


#創建識別模型,使用LBPHFace演算法識別,Confidence評分低於50是可靠,0是完全契合

model = cv2.face.LBPHFaceRecognizer_create()

#創建識別模型,使用FisherFace演算法識別,Confidence評分低於4000是可靠,0是完全契合

#model = cv2.face.FisherFaceRecognizer_create()

#創建識別模型,使用EigenFace演算法識別,Confidence評分低於4000是可靠,0是完全契合

#model = cv2.face.EigenFaceRecognizer_create()


faces = []   # 儲存人臉位置大小的串列

ids = []     # 記錄該人臉 id 的串列


directory="./dataset/"

face_list=''


for _,subdir,_ in os.walk(directory):

    for i in range(len(subdir)):

        path=directory + subdir[i] + '/'

        face_list+=  str(i) + ' ' + subdir[i] +'\n'

        for j in os.listdir(path):

            filename=path+j

            print(filename)

            img= cv2.imdecode(np.fromfile(filename,dtype=np.uint8),-1)

            #img = cv2.imread(filename)                   # 依序開啟每一張

            gray = cv2.cvtColor(img, cv2.COLOR_BGR2GRAY)  # 色彩轉換成黑白

            img_np = np.array(gray,'uint8')               # 轉換成指定編碼的 numpy 陣列

            face = detector.detectMultiScale(gray)        # 擷取人臉區域

            for(x,y,w,h) in face:

                faces.append(img_np[y:y+h,x:x+w])         # 記錄人臉的位置和大小內像素的數值

                ids.append(i)                             # 記錄人臉對應的 id,只能是整數

#將人臉標籤寫成文字檔

path = 'face_label.txt'

f = open(path, 'w')

f.write(face_list)

f.close()


print('training...')                              # 提示開始訓練

model.train(faces,np.array(ids))                  # 開始訓練

model.save('face.yml')                            # 訓練完成儲存為 face.yml

print('ok!')

import cv2

import numpy as np

from PIL import Image,ImageDraw,ImageFont


def putchinesetext(frame_img,text,pos,color):

    #將opencv陣列格式轉成PIL影像

    img=Image.fromarray(cv2.cvtColor(frame_img, cv2.COLOR_BGR2RGB))

    myfont=ImageFont.truetype('wt014.ttf',14)

    draw=ImageDraw.Draw(img)

    draw.text(pos,text,color,font=myfont)

    return cv2.cvtColor(np.array(img), cv2.COLOR_RGB2BGR)


# 啟用訓練人臉模型方法

recognizer = cv2.face.LBPHFaceRecognizer_create()         

recognizer.read('face.yml')

cascade_path = "haarcascade_frontalface_default.xml" 

face_cascade = cv2.CascadeClassifier(cascade_path)


cap = cv2.VideoCapture(0)


while True:

    ret, img = cap.read()

    if not ret:

        print("Cannot receive frame")

        break

    img = cv2.resize(img,(320,240))              # 縮小尺寸,加快辨識效率

    gray = cv2.cvtColor(img,cv2.COLOR_BGR2GRAY)  # 轉換成黑白

    faces = face_cascade.detectMultiScale(gray)  # 追蹤人臉 ( 目的在於標記出外框 )


    # 建立姓名和 id 的對照表

    name={}

    f = open('face_label.txt')

    for line in f.readlines():

        id,sname=line.split()

        name[id]=sname

    f.close


    # 依序判斷每張臉屬於哪個 id

    for(x,y,w,h) in faces:

        cv2.rectangle(img,(x,y),(x+w,y+h),(0,255,0),2)            # 標記人臉外框

        idnum,confidence = recognizer.predict(gray[y:y+h,x:x+w])  # 取出 id 號碼以及信心指數 confidence

        if confidence < 80:

            # 如果信心指數大於 60,取得對應的名字

            text = name[str(idnum)] + str(round(confidence,2))

        else:

            text = 'Unknow'                                          # 不然名字就是 ???

        # 在人臉外框旁加上名字

        print(round(confidence,2))

        cimg=putchinesetext(img,text,(x,y-20),(0,255,0))


    cv2.imshow('oxxostudio', cimg)

    if cv2.waitKey(5) == ord('q'):

        break    # 按下 q 鍵停止

cap.release()

cv2.destroyAllWindows()

Face Detection with FaceNet-Pytorch

from facenet_pytorch import MTCNN

import torch

import cv2


device = torch.device('cuda:0' if torch.cuda.is_available() else 'cpu')

mtcnn=MTCNN(keep_all=True,device=device)


def Picture(window_name,img): 

        faces,prob_list=mtcnn(img, return_prob=True)

        if faces is not None:        

            boxes,_=mtcnn.detect(img)

            print("檢測人臉數目:",len(boxes))

            for i,box in enumerate(boxes):

                if prob_list[i]>0.9:

                    x1,y1=int(box.tolist()[0]),int(box.tolist()[1])

                    x2,y2=int(box.tolist()[2]),int(box.tolist()[3])

                    cv2.rectangle(img, (x1,y1),(x2,y2), (0, 255, 0), 2)

                    cv2.putText(img, f"confidence: {str(round(prob_list[i], 3))}", (x1, y1 - 10), cv2.FONT_HERSHEY_SIMPLEX, 0.5, (0, 255, 255), 2)

        cv2.imwrite(f'{file[:-4]}_output{file[-4:]}',img)

        cv2.imshow(window_name, img)

        cv2.waitKey(0)



if __name__ == '__main__':

    file='demo.jpg'

    img=cv2.imread(file)

    Picture("Face_Detect",img) 



from facenet_pytorch import MTCNN

import torch

import cv2


device = torch.device('cuda:0' if torch.cuda.is_available() else 'cpu')

mtcnn=MTCNN(keep_all=True,device=device)


def CatchVideo(window_name):

    cap = cv2.VideoCapture(0)

    while True:

        ret, frame = cap.read()

        if ret==False: 

            break

 

        faces,prob_list=mtcnn(frame, return_prob=True)


        if faces is not None:        

            boxes,_=mtcnn.detect(frame)

            print("檢測人臉數目:",len(boxes))

            for i,box in enumerate(boxes):

                if prob_list[i]>0.9:

                    x1,y1=int(box.tolist()[0]),int(box.tolist()[1])

                    x2,y2=int(box.tolist()[2]),int(box.tolist()[3])

                    cv2.rectangle(frame, (x1,y1),(x2,y2), (0, 255, 0), 2)

                    cv2.putText(frame, f"confidence: {str(round(prob_list[i], 3))}", (x1, y1 - 10), cv2.FONT_HERSHEY_SIMPLEX, 0.5, (0, 255, 255), 2)


        cv2.imshow(window_name, frame)


        if cv2.waitKey(10) & 0xFF == ord('q'):

            break

        

    #釋放鏡頭

    cap.release()

    #關閉視窗

    cv2.destroyAllWindows()



if __name__ == '__main__':

    CatchVideo("Face_Detect") 

Face Recognition with  FaceNet-Pytorch

from facenet_pytorch import MTCNN, InceptionResnetV1

import torch

from torch.utils.data import DataLoader

from torchvision import datasets

import os


#如果是Windows系列,wokers=0,否則(posix, java),workers=4

workers = 0 if os.name == 'nt' else 4


device = torch.device('cuda:0' if torch.cuda.is_available() else 'cpu')

print('Running on device: {}'.format(device))


mtcnn = MTCNN(

    image_size=160, margin=0, min_face_size=20,

    thresholds=[0.6, 0.7, 0.7], factor=0.709, post_process=True,

    device=device

)


resnet = InceptionResnetV1(pretrained='vggface2').eval().to(device)



def collate_fn(x):

    return x[0]


dataset = datasets.ImageFolder('./dataset/test_images')

dataset.idx_to_class={i:c for c,i in dataset.class_to_idx.items()}

loader = DataLoader(dataset, collate_fn=collate_fn, num_workers=workers)



#從圖像取出160x160的人臉

aligned = []

#姓名列表

names = []


for x, y in loader:

    path=f'./dataset/aligned/{dataset.idx_to_class[y]}/'

    if not os.path.exists(path):

        i=1

        os.mkdir(path)


    x_aligned, prob = mtcnn(x, return_prob=True,save_path=path + '{}.jpg'.format(i))

    i+=1

    if x_aligned is not None:

        print(f'Face detected with probability: {prob:8f}')

        aligned.append(x_aligned)

        names.append(dataset.idx_to_class[y])


aligned = torch.stack(aligned).to(device)

#提取所有人臉的特徵向量,每個向量的長度512

embeddings = resnet(aligned).detach().cpu()


#儲存模型

data=[embeddings,names]

torch.save(data,'nths.pt')

from facenet_pytorch import MTCNN, InceptionResnetV1

from PIL import Image,ImageDraw,ImageFont

import torch

import cv2

import numpy as np


device = torch.device('cuda:0' if torch.cuda.is_available() else 'cpu')

mtcnn=MTCNN(keep_all=True,device=device)

resnet=InceptionResnetV1(pretrained='vggface2').eval().to(device)

load_data=torch.load("nths.pt")


embeddings=load_data[0]

names=load_data[1]


def detect_frame(frame_img):

    global who

    #將opencv陣列格式轉成PIL影像

    img=Image.fromarray(cv2.cvtColor(frame_img, cv2.COLOR_BGR2RGB))

    faces,prob_list=mtcnn(img, return_prob=True)

    if faces is not None:        

        boxes,_=mtcnn.detect(img)       

        draw=ImageDraw.Draw(img)

        myfont=ImageFont.truetype('wt014.ttf',14)

        print("檢測人臉數目:",len(boxes))

        for i,box in enumerate(boxes):

            draw.rectangle(box.tolist(),outline=(255,0,0))

            face_embedding=resnet(faces[i].unsqueeze(0).to(device))

            #將人臉特徵距離儲存到probs陣列中

            probs=[(face_embedding - embeddings[i]).norm().item() for i in range(embeddings.size()[0])]

            #取得最短距離的索引

            index=probs.index(min(probs))

            if prob_list[i]>0.99:

                name=names[index]

            else:

                name="Unknow"

            draw.text((int(box[0]),int(box[1])),str(name)+str(round(prob_list[i],2)),fill=(255,0,0),font=myfont)

    return img



def CatchVideo(window_name):

    cap = cv2.VideoCapture(0)

    while True:

        ret, frame = cap.read()

        if ret==False: 

            break

        myframe=detect_frame(frame)

        #將PIL影像轉回opencv陣列格式

        cvimg = cv2.cvtColor(np.array(myframe), cv2.COLOR_RGB2BGR)

        cv2.imshow(window_name, cvimg)

        if cv2.waitKey(10) & 0xFF == ord('q'):

            break

        

    #釋放鏡頭

    cap.release()


    #關閉視窗

    cv2.destroyAllWindows()


if __name__ == '__main__':

    CatchVideo("Vedio") 


Facial Landmark: MTCNN

import time

import cv2

import mtcnn


def CatchVideo(window_name):

    detector = mtcnn.MTCNN()

    time.sleep(2.0)

    cap = cv2.VideoCapture(0)


    while True:

        ret, frame = cap.read()

        if ret==False:

            break

        rgb = cv2.cvtColor(frame, cv2.COLOR_BGR2RGB)

        faces = detector.detect_faces(rgb)

       

        for face in faces:

            (x, y, w, h) = face['box']

            keypoints = face['keypoints']

            conf = face['confidence']


            cv2.rectangle(frame, (x, y), (x + w, y + h), (0, 255, 0), 2)

            cv2.putText(frame, f"confidence: {str(round(conf, 3))}", (x, y - 10), cv2.FONT_HERSHEY_SIMPLEX, 0.5, (0, 255, 255), 2)


            for (s0, s1) in keypoints.values():

                cv2.circle(frame, (s0, s1), 2, (0, 0, 255), -1)


        cv2.imshow(window_name, frame)

       

        if cv2.waitKey(10) & 0xFF == ord('q'):

            break


    #釋放鏡頭

    cap.release()


    #關閉視窗

    cv2.destroyAllWindows()



if __name__ == '__main__':

    CatchVideo("Vedio")