赞
踩
目录
在这篇博客文章中,我们将深入探讨如何从头开始,利用深度学习模型Xception来实现戴口罩人脸表情识别的挑战。这个问题在当今社会中尤其具有现实意义,因为全球公众面临着新冠病毒的威胁,口罩成为我们日常生活的必备之物,而在戴口罩的情况下识别人脸表情具有许多重要的应用价值,如提升社交软件的用户体验,提高公共场所的安全等。
我们将从基础开始,解释Xception模型的工作原理,然后详细阐述如何使用该模型进行戴口罩人脸表情识别的训练与实现。文章内容将包括数据收集、数据预处理、模型训练、优化,以及模型测试的详细步骤。
无论你是一位有经验的深度学习研究者,还是对人工智能应用感兴趣的初学者,我们都相信你将在这篇文章中找到新的知识和灵感。希望通过我们的介绍,你能更好地理解并应用深度学习技术,解决实际生活中的问题。
本项目基于fer2013人脸表情数据集,它于2013年国际机器学习会议(ICML)上推出,并成为比较表情识别模型性能的基准之一,同时也作为了2013年Kaggle人脸识别比赛的数据。Fer2013包含28709张训练集图像、3589张公开测试集图像和3589张私有测试集图像,每张图像为4848大小的灰度图片,如下图所示。Fer2013数据集中由有生气(angry)、厌恶(disgust)、恐惧(fear)、开心(happy)、难过(sad)、惊讶(surprise)和中性(neutral)七个类别组成。由于这个数据集大多是通过爬虫在互联网上进行爬取所得,因此存在一定的误差性。
本项目使用face-mask对人脸添加口罩,简单介绍一下face-mask是基于dlib和face_recognition两大人脸检测的库实现的人脸关键点检测的方法。处理完成后共得到11870张训练集,3016张测试集(原数据集存在一些不能被face-mask所识别到的人脸)。经过实验发现sad、disgust、anger眼部特征十分相识,因此本试验只进行对anger、fear、happy、surprise的分类。
pip install face-mask
- # -*- coding: utf-8 -*-
- import os
- import numpy as np
- from PIL import Image, ImageFile
-
- __version__ = '0.3.0'
-
-
- IMAGE_DIR = os.path.join(os.path.dirname(os.path.abspath(__file__)), 'images')
- DEFAULT_IMAGE_PATH = os.path.join(IMAGE_DIR, 'default-mask.png')
- BLACK_IMAGE_PATH = os.path.join(IMAGE_DIR, 'black-mask.png')
- BLUE_IMAGE_PATH = os.path.join(IMAGE_DIR, 'blue-mask.png')
- RED_IMAGE_PATH = os.path.join(IMAGE_DIR, 'red-mask.png')
-
-
- class FaceMasker:
- KEY_FACIAL_FEATURES = ('nose_bridge', 'chin')
-
- def __init__(self, face_path, mask_path, show=False, model='hog'):
- self.face_path = face_path
- self.mask_path = mask_path
- self.show = show
- self.model = model
- self._face_img: ImageFile = None
- self._mask_img: ImageFile = None
-
- def mask(self):
- import face_recognition
-
- face_image_np = face_recognition.load_image_file(self.face_path)
- face_locations = face_recognition.face_locations(face_image_np, model=self.model)
- face_landmarks = face_recognition.face_landmarks(face_image_np, face_locations)
- self._face_img = Image.fromarray(face_image_np)
- self._mask_img = Image.open(self.mask_path)
-
- found_face = False
- for face_landmark in face_landmarks:
- # check whether facial features meet requirement
- skip = False
- for facial_feature in self.KEY_FACIAL_FEATURES:
- if facial_feature not in face_landmark:
- skip = True
- break
- if skip:
- continue
-
- # mask face
- found_face = True
- self._mask_face(face_landmark)
-
- if found_face:
- if self.show:
- self._face_img.show()
-
- # save
- self._save()
- else:
- print('Found no face.')
-
- def _mask_face(self, face_landmark: dict):
- nose_bridge = face_landmark['nose_bridge']
- nose_point = nose_bridge[len(nose_bridge) * 1 // 4]
- nose_v = np.array(nose_point)
-
- chin = face_landmark['chin']
- chin_len = len(chin)
- chin_bottom_point = chin[chin_len // 2]
- chin_bottom_v = np.array(chin_bottom_point)
- chin_left_point = chin[chin_len // 8]
- chin_right_point = chin[chin_len * 7 // 8]
-
- # split mask and resize
- width = self._mask_img.width
- height = self._mask_img.height
- width_ratio = 1.2
- new_height = int(np.linalg.norm(nose_v - chin_bottom_v))
-
- # left
- mask_left_img = self._mask_img.crop((0, 0, width // 2, height))
- mask_left_width = self.get_distance_from_point_to_line(chin_left_point, nose_point, chin_bottom_point)
- mask_left_width = int(mask_left_width * width_ratio)
- mask_left_img = mask_left_img.resize((mask_left_width, new_height))
-
- # right
- mask_right_img = self._mask_img.crop((width // 2, 0, width, height))
- mask_right_width = self.get_distance_from_point_to_line(chin_right_point, nose_point, chin_bottom_point)
- mask_right_width = int(mask_right_width * width_ratio)
- mask_right_img = mask_right_img.resize((mask_right_width, new_height))
-
- # merge mask
- size = (mask_left_img.width + mask_right_img.width, new_height)
- mask_img = Image.new('RGBA', size)
- mask_img.paste(mask_left_img, (0, 0), mask_left_img)
- mask_img.paste(mask_right_img, (mask_left_img.width, 0), mask_right_img)
-
- # rotate mask
- angle = np.arctan2(chin_bottom_point[1] - nose_point[1], chin_bottom_point[0] - nose_point[0])
- rotated_mask_img = mask_img.rotate(angle, expand=True)
-
- # calculate mask location
- center_x = (nose_point[0] + chin_bottom_point[0]) // 2
- center_y = (nose_point[1] + chin_bottom_point[1]) // 2
-
- offset = mask_img.width // 2 - mask_left_img.width
- radian = angle * np.pi / 180
- box_x = center_x + int(offset * np.cos(radian)) - rotated_mask_img.width // 2
- box_y = center_y + int(offset * np.sin(radian)) - rotated_mask_img.height // 2
-
- # add mask
- self._face_img.paste(mask_img, (box_x, box_y), mask_img)
-
- def _save(self):
- path_splits = os.path.splitext(self.face_path)
- new_face_path = path_splits[0] + '-with-mask' + path_splits[1]
- self._face_img.save(new_face_path)
- print(f'Save to {new_face_path}')
-
- @staticmethod
- def get_distance_from_point_to_line(point, line_point1, line_point2):
- distance = np.abs((line_point2[1] - line_point1[1]) * point[0] +
- (line_point1[0] - line_point2[0]) * point[1] +
- (line_point2[0] - line_point1[0]) * line_point1[1] +
- (line_point1[1] - line_point2[1]) * line_point1[0]) / \
- np.sqrt((line_point2[1] - line_point1[1]) * (line_point2[1] - line_point1[1]) +
- (line_point1[0] - line_point2[0]) * (line_point1[0] - line_point2[0]))
- return int(distance)
-
-
- if __name__ == '__main__':
- FaceMasker("./face/1.jpg", DEFAULT_IMAGE_PATH, True, 'hog').mask()
Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。