Python实战——为人脸照片添加口罩

发表于:2022-5-07 10:01

字体: | 上一篇 | 下一篇 | 我要投稿

 作者:aikutwo    来源:稀土掘金

  数据集展示
  数据集来源:使用了开源数据集FaceMask_CelebA

  部分人脸数据集:
图片来源于网络

  口罩样本数据集:
图片来源于网络

  为人脸照片添加口罩代码
  这部分有个库face_recognition需要安装,如果之前没有用过的小伙伴可能得费点功夫。
  Face Recognition 库主要封装了dlib这一 C++ 图形库,通过 Python 语言将它封装为一个非常简单就可以实现人脸识别的 API 库,屏蔽了人脸识别的算法细节,大大降低了人脸识别功能的开发难度。
  #!/usr/bin/env python
  # -*- coding: utf-8 -*-
  # @Author  : 2014Vee
  import os
  import numpy as np
  from PIL import Image, ImageFile
  __version__ = '0.3.0'
  IMAGE_DIR = os.path.dirname('E:/play/FaceMask_CelebA-master/facemask_image/')
  WHITE_IMAGE_PATH = os.path.join(IMAGE_DIR, 'front_14.png')
  BLUE_IMAGE_PATH = os.path.join(IMAGE_DIR, 'front_14.png')
  SAVE_PATH = os.path.dirname('E:/play/FaceMask_CelebA-master/save/synthesis/')
  SAVE_PATH2 = os.path.dirname('E:/play/FaceMask_CelebA-master/save/masks/')
  class FaceMasker:
      KEY_FACIAL_FEATURES = ('nose_bridge', 'chin')
      def __init__(self, face_path, mask_path, white_mask_path, save_path, save_path2, model='hog'):
          self.face_path = face_path
          self.mask_path = mask_path
          self.save_path = save_path
          self.save_path2 = save_path2
          self.white_mask_path = white_mask_path
          self.model = model
          self._face_img: ImageFile = None
          self._black_face_img = None
          self._mask_img: ImageFile = None
          self._white_mask_img = 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)
          self._white_mask_img = Image.open(self.white_mask_path)
          self._black_face_img = Image.new('RGB', self._face_img.size, 0)
          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:
              # 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)
          # split mask and resize
          width = self._white_mask_img.width
          height = self._white_mask_img.height
          width_ratio = 1.2
          new_height = int(np.linalg.norm(nose_v - chin_bottom_v))
          # left
          mask_left_img = self._white_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._white_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._black_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 = self.save_path + '/' + os.path.basename(self.face_path) + '-with-mask' + path_splits[1]
          # new_face_path2 = self.save_path2 + '/' + os.path.basename(self.face_path) + '-binary' + path_splits[1]
          new_face_path = self.save_path + '/' + os.path.basename(self.face_path) + '-with-mask' + path_splits[1]
          new_face_path2 = self.save_path2 + '/'  + os.path.basename(self.face_path) + '-binary' + path_splits[1]
          self._face_img.save(new_face_path)
          self._black_face_img.save(new_face_path2)
      #         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)
      # FaceMasker("/home/aistudio/data/人脸.png", WHITE_IMAGE_PATH, True, 'hog').mask()
  from pathlib import Path
  images = Path("E:/play/FaceMask_CelebA-master/bbox_align_celeba").glob("*")
  cnt = 0
  for image in images:
      if cnt < 1:
          cnt += 1
          continue
      FaceMasker(image, BLUE_IMAGE_PATH, WHITE_IMAGE_PATH, SAVE_PATH, SAVE_PATH2, 'hog').mask()
      cnt += 1
      print(f"正在处理第{cnt}张图片,还有{99 - cnt}张图片")

  掩膜生成代码
  这部分其实就是对使用的口罩样本的二值化,因为后续要相关模型会用到。

  import os
  from PIL import Image
  # 源目录
  # MyPath = 'E:/play/FaceMask_CelebA-master/facemask_image/'
  MyPath = 'E:/play/FaceMask_CelebA-master/save/masks/'
  # 输出目录
  OutPath = 'E:/play/FaceMask_CelebA-master/save/Binarization/'
  def processImage(filesoure, destsoure, name, imgtype):
      '''
      filesoure是存放待转换图片的目录
      destsoure是存在输出转换后图片的目录
      name是文件名
      imgtype是文件类型
      '''
      imgtype = 'bmp' if imgtype == '.bmp' else 'png'
      # 打开图片
      im = Image.open(filesoure + name)
      # =============================================================================
      #     #缩放比例
      #     rate =max(im.size[0]/640.0 if im.size[0] > 60 else 0, im.size[1]/1136.0 if im.size[1] > 1136 else 0)
      #     if rate:
      #         im.thumbnail((im.size[0]/rate, im.size[1]/rate))
      # =============================================================================
      img = im.convert("RGBA")
      pixdata = img.load()
      # 二值化
      for y in range(img.size[1]):
          for x in range(img.size[0]):
              if pixdata[x, y][0] < 90:
                  pixdata[x, y] = (0, 0, 0, 255)
      for y in range(img.size[1]):
          for x in range(img.size[0]):
              if pixdata[x, y][1] < 136:
                  pixdata[x, y] = (0, 0, 0, 255)
      for y in range(img.size[1]):
          for x in range(img.size[0]):
              if pixdata[x, y][2] > 0:
                  pixdata[x, y] = (255, 255, 255, 255)
      img.save(destsoure + name, imgtype)
  def run():
      # 切换到源目录,遍历源目录下所有图片
      os.chdir(MyPath)
      for i in os.listdir(os.getcwd()):
          # 检查后缀
          postfix = os.path.splitext(i)[1]
          name = os.path.splitext(i)[0]
          name2 = name.split('.')
          if name2[1] == 'jpg-binary' or name2[1] == 'png-binary':
              processImage(MyPath, OutPath, i, postfix)
  if __name__ == '__main__':
      run()

  本文内容不用于商业目的,如涉及知识产权问题,请权利人联系51Testing小编(021-64471599-8017),我们将立即处理
《2023软件测试行业现状调查报告》独家发布~

关注51Testing

联系我们

快捷面板 站点地图 联系我们 广告服务 关于我们 站长统计 发展历程

法律顾问:上海兰迪律师事务所 项棋律师
版权所有 上海博为峰软件技术股份有限公司 Copyright©51testing.com 2003-2024
投诉及意见反馈:webmaster@51testing.com; 业务联系:service@51testing.com 021-64471599-8017

沪ICP备05003035号

沪公网安备 31010102002173号