
人脸识别
人脸识别应该不是什么新奇的东西,但是就看大家谁做的更好更快了。今天和大家一起聊一聊人脸识别基本打法。也就是用 mtcnn 做人脸识别,将人脸识别通过一个框识别出来,然后将输出人脸进行处理为 图片输入到 facenet 中提取出 128 维特征点,最后计算不同图片经过上面处理得到 128 特征点计算距离来得到相似度,根据阈值来判断是否图片出现人为同一个人。这就是人脸的 pipeline。
import cv2
import numpy as np
import matplotlib.pyplot as plt
import tensorflow as tf
img = cv2.imread("data/imgs/naruto.jpg")
img = cv2.cvtColor(img,cv2.COLOR_BGR2RGB)
plt.imshow(img)

今天我们来聊一聊人脸识别,当我们人类看到上面图片,一眼就可以认出这就是 naruto,是我个人比较喜欢一个动画中的主角。
face_box = [(450,0),(850,520)]
thickness = 4
lineType = 4
cv2.rectangle(img,face_box[0],face_box[1],(255,0,0),thickness,lineType)
plt.imshow(img)

那么我们是如何完成这件事的呢?首先我们是找到人脸位置,要找到人脸位置我们就用到 mtcnn 来识别人脸的位置。
from mtcnn import MTCNN
mtcnn
我们利用图像金子塔生成不同尺寸大小一系列图片,目的就是为了检查大小不同人脸。下面代码我们用 opencv 简单实现了一个如何获取一张图片一系列大小不同图片。
img = cv2.imread('data/imgs/naruto.jpg')
img = cv2.cvtColor(img,cv2.COLOR_BGR2RGB)
up_img = cv2.pyrUp(img)
img_1 = cv2.pyrDown(img)
img_2 = cv2.pyrDown(img_1)
plt.imshow(up_img)
plt.show()
plt.imshow(img)
plt.show()
plt.imshow(img_1)
plt.show()
plt.imshow(img_2)
plt.show()




Pnet
然后将图片金字塔生成图输入到Pnet,生成一堆人脸识别候选框,这些人脸候选框都是相对于原图的如下图。
img = cv2.imread("data/imgs/naruto.jpg")
img = cv2.cvtColor(img,cv2.COLOR_BGR2RGB)
# plt.imshow(img)
face_box = [(450,0,850,520),(500,0,750,500),(500,10,850,500),(600,200,700,300),(600,200,300,200),(500,300,300,200)]
thickness = 4
lineType = 4
for i in range(len(face_box)):
cv2.rectangle(img,(face_box[i][0],face_box[i][1]),(face_box[i][2],face_box[i][3]),(255,0,0),thickness,lineType)
plt.imshow(img)
# plt.imshow(img)

Rnet
然后将这些人脸候选框截取图片输入到 Rnet 中,Rnet 会对图片中是否存在人脸进行判断评分。同时还对于原有人脸框进行修正。经过 Rnet 我们会得到一些相对正确的人脸框。
img = cv2.imread("data/imgs/naruto.jpg")
img = cv2.cvtColor(img,cv2.COLOR_BGR2RGB)
# plt.imshow(img)
face_box = [(450,0,850,520),(500,0,750,500),(500,10,850,500)]
thickness = 4
lineType = 4
for i in range(len(face_box)):
cv2.rectangle(img,(face_box[i][0],face_box[i][1]),(face_box[i][2],face_box[i][3]),(255,0,0),thickness,lineType)
plt.imshow(img)

Onet
然后我们再次将 Rnet 筛选人脸候选框传入到 Onet 中,Onet 中是最精细网络筛选,其实也就是重复 Rnet 工作,对人脸框进行评分并进行修正。完成了 Onet 我们也就得到人脸位置。
img = cv2.imread("data/imgs/naruto.jpg")
img = cv2.cvtColor(img,cv2.COLOR_BGR2RGB)
# plt.imshow(img)
face_box = [(450,0,850,520)]
thickness = 4
lineType = 4
for i in range(len(face_box)):
cv2.rectangle(img,(face_box[i][0],face_box[i][1]),(face_box[i][2],face_box[i][3]),(255,255,0),thickness,lineType)
plt.imshow(img)

Facenet
Facenet 就是输入一张人脸,然后进行特征提取,最终输出 128 维特征向量,通过比较特征向量我们辨识谁是谁。对比向量是我们事先准备好数据库,这里有代表标签的数据。然后将 facenet 提取出 128 维向量和数据库所有人脸向量进行比对,最终选择距离最短而且需要小于一定阈值的作为检测结果。
andy_img = cv2.cvtColor(cv2.imread, cv2.COLOR_BGR2RGB)
detector = MTCNN()
recognizer = detector.detect_faces(andy_img)
plt.imshow(andy_img)

读取 Andy 图片,我们这里使用 opencv 读取图片,在 opencv 中默认颜色是 BGR 模式,但是正常的模式都是 RGB 模式所以我们需要调用 opencv 提供的 cvtColor 方法将图片数据格式转换为 RGB。然后调用 MTCHH 的 detect_faces 方法,这个方法会返回一个人脸对象集合,这里图片只有 Andy 一张脸,所以只有人脸对象,输出一下,看看检测人脸对象都包含哪些信息。
recognizer
[{'box': [77, 130, 257, 336],
'confidence': 0.9999997615814209,
'keypoints': {'left_eye': (144, 259),
'right_eye': (264, 251),
'nose': (207, 340),
'mouth_left': (166, 393),
'mouth_right': (257, 389)}}]
通常检测人脸会返回一个人脸框、和 5 个特征点分别是左右眼、鼻子和嘴角。下面我们将这些点显示出来
bounding_box = recognizer[0]['box']
right_eye = recognizer[0]['keypoints']['right_eye']
left_eye = recognizer[0]['keypoints']['left_eye']
nose = recognizer[0]['keypoints']['nose']
mouth_left = recognizer[0]['keypoints']['mouth_left']
mouth_right = recognizer[0]['keypoints']['mouth_right']
cv2.circle(andy_img,(right_eye[0],right_eye[1]),5,(0, 255, 0),-1 )
cv2.circle(andy_img,(left_eye[0],left_eye[1]),5,(0, 255, 0),-1 )
cv2.circle(andy_img,(nose[0],nose[1]),5,(0, 255, 0),-1 )
cv2.circle(andy_img,(mouth_left[0],mouth_left[1]),5,(0, 255, 0),-1 )
cv2.circle(andy_img,(mouth_right[0],mouth_right[1]),5,(0, 255, 0),-1 )
print(recognizer[0].get('box'))
andy_face_box = recognizer[0].get('box')
cv2.rectangle(andy_img,(andy_face_box[0],andy_face_box[1]),((andy_face_box[0] + andy_face_box[2]),(andy_face_box[1]+andy_face_box[3])),(255,255,0),thickness,lineType)
plt.imshow(andy_img)
[77, 130, 257, 336]

import os
face_list = os.listdir("face_dataset")
print(face_list)
['.DS_Store', 'andy.jpg', 'zidea.png']
face_encoding = []
face_name = []
for face in face_list:
if face.startswith("."):
continue
name = face.split(".")[0]
img = cv2.imread("face_dataset/"+face)
# print(img)
img = cv2.cvtColor(img, cv2.COLOR_BGR2RGB)
res = detector.detect_faces(andy_img)
rectangle = res[0]
接下来工作我们是处理这些人脸,根据人脸框将这些人脸截取出来,缩放到 正方形后输入 facenet 网络机会得到 128 维特征向量,然后我们是通过KNN 等算法计算两个张图片的距离来计算相似度。今天我们换一中用 PCA 来降维分析。
from PIL import Image
img = Image.open("face_dataset/andy.jpg")
cropped = img.crop((andy_face_box[0],andy_face_box[1],(andy_face_box[0] + andy_face_box[2]),
(andy_face_box[1]+andy_face_box[3])))
plt.imshow(cropped)

cropped.save("face_dataset/andy_face.jpg")
我们在图像中有时候人脸可能是侧脸和歪着的脸,这些脸需要进行校正后在输入到 facenet 中进行特征提取,接下来我们就来做这件事。看如何通过特征点来校正人脸。这里还是用 Andy 图片作为例子来实现人脸校正的过程。
andy_img_1 = cv2.imread("face_dataset/andy_01.jpg")
# print(andy_img_1)
andy_img_1 = cv2.cvtColor(andy_img_1,cv2.COLOR_BGR2RGB)
recognizer_1 = detector.detect_faces(andy_img_1)
recognizer_1
[{'box': [103, 80, 182, 246],
'confidence': 0.9997475743293762,
'keypoints': {'left_eye': (153, 195),
'right_eye': (233, 179),
'nose': (203, 240),
'mouth_left': (175, 282),
'mouth_right': (246, 268)}}]
andy_1_face_box = recognizer_1[0].get('box')
cv2.rectangle(andy_img_1,(
andy_1_face_box[0],
andy_1_face_box[1]),
((andy_1_face_box[0] + andy_1_face_box[2]),
(andy_1_face_box[1]+andy_1_face_box[3])),
(255,255,0),thickness,lineType)
plt.imshow(andy_img_1)

img = Image.open("face_dataset/andy_01.jpg")
cropped_01 = img.crop((andy_1_face_box[0],andy_1_face_box[1],(andy_1_face_box[0] + andy_1_face_box[2]),
(andy_1_face_box[1]+andy_1_face_box[3])))
plt.imshow(cropped_01)
cropped_01.save("face_dataset/andy_face_01.jpg")

img_01 = cv2.imread("face_dataset/andy_face_01.jpg")
img_01 = cv2.cvtColor(img_01,cv2.COLOR_BGR2RGB)
plt.imshow(img_01)

right_eye = recognizer_1[0]['keypoints']['right_eye']
left_eye = recognizer_1[0]['keypoints']['left_eye']
nose = recognizer_1[0]['keypoints']['nose']
mouth_left = recognizer_1[0]['keypoints']['mouth_left']
mouth_right = recognizer_1[0]['keypoints']['mouth_right']
cv2.circle(img_01,(right_eye[0] - andy_1_face_box[0],right_eye[1] - andy_1_face_box[1]),5,(0, 255, 0),-1 )
cv2.circle(img_01,(left_eye[0] - andy_1_face_box[0],left_eye[1] - andy_1_face_box[1]),5,(0, 255, 0),-1 )
cv2.circle(img_01,((nose[0] - andy_1_face_box[0]) ,(nose[1] - andy_1_face_box[1])),5,(0, 255, 0),-1 )
cv2.circle(img_01,((mouth_left[0] - andy_1_face_box[0]),(mouth_left[1] - andy_1_face_box[1])),5,(0, 255, 0),-1 )
cv2.circle(img_01,((mouth_right[0] - andy_1_face_box[0]),(mouth_right[1]- andy_1_face_box[1])),5,(0, 255, 0),-1 )
plt.imshow(img_01)

今天就到这里吧,有时间继续。
网友评论