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

效果展示

数据集展示

数据集来源:使用了开源数据集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()

祝大家周末愉快~~~

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
### 回答1: Python项目实战:使用PySpark对大数据进行分析 PySpark是一个基于Python的Spark API,它提供了一种分布式计算框架,可以用于处理大规模数据集。使用PySpark,可以轻松地对大数据进行分析和处理,从而提高数据分析的效率和准确性。 在实际项目中,使用PySpark进行大数据分析可以帮助我们更好地理解数据,发现数据中的规律和趋势,从而为业务决策提供更准确的依据。同时,PySpark还可以帮助我们处理数据中的异常值和缺失值,提高数据的质量和可靠性。 总之,使用PySpark进行大数据分析是一项非常有价值的技能,可以帮助我们更好地应对现代数据分析的挑战。 ### 回答2: 随着互联网的飞速发展,数据的产生量越来越大,如何处理大数据是一个非常重要的问题。Python是目前主流的编程语言之一,尤其是在数据科学、机器学习、人工智能等领域广受欢迎。pyspark是Apache Spark的Python API,它提供了一个基于内存的分布式计算框架,可以处理大规模数据集,并且具有高性能、易于使用、可扩展的特点。 使用pyspark对大数据进行分析,我们可以使用Spark的分布式内存计算引擎,在集群中并行计算数据,并为大数据应用提供高效的解决方案。pyspark提供了丰富的API,可以实现对大数据的探索性分析、数据预处理、特征工程、模型训练和预测等操作。 在进行大数据分析前,首先需要创建SparkSession对象,这个对象是通往Spark的入口。接下来可以读取数据集,并进行一系列的数据清洗、转换等操作。常用的数据处理操作包括:数据过滤、数据映射、数据排序、数据聚合等。 在进行特征工程时,pyspark提供了大量的内置函数和转换操作,如Tokenizer、StopWordsRemover、VectorAssembler、StringIndexer等。可以使用这些函数将原始数据集转换为模型可用的特征向量。 对于大规模的数据集训练机器学习模型,pyspark提供了分布式的算法库,如线性回归、逻辑回归、决策树、随机森林、支持向量机、Gradient-Boosted Trees等。可以选择合适的算法库进行数据建模和预测,同时也可以根据需要扩展定制化算法库。 pyspark的强大功能让我们能够处理超大规模数据集,从而能够快速、高效地进行大数据分析。因此,学习pyspark对于数据科学家和数据工程师来说变得越来越重要,对于数据分析和挖掘等领域也有着巨大的应用前景。 ### 回答3: 随着大数据时代的到来,大数据分析已经成为了一个趋势。在处理大量数据时,传统的数据处理方式已经无法满足需求。而pyspark则成为了处理大数据的良好工具之一。pyspark是一个基于Apache Spark框架的Python API。采用大数据技术将数据分布式并行处理,具有高效、快速、可靠的特点。在处理大数据时,pyspark能使数据处理过程变得更便捷和高效。 使用pyspark进行大数据分析时,需要先了解spark框架的运行方式。Spark框架是由一个 Driver程序和多个Executor程序组成。Driver程序负责任务分配和控制,而Executor程序负责具体的数据分析。在Driver程序中,通过pyspark编写代码进行数据处理和分析。数据处理的过程包括数据清洗、转换、过滤和计算等步骤。而在数据分析时,采用了三个重要的API:RDD、DataFrame和DataSet。 其中RDD是一种数据结构,表示“弹性分布式数据集”。RDD的特点是不可变性、分布式、容错性和操作性等。通过RDD来重复读取数据,对数据集进行处理和分析等操作。DataFrame是一种分布式数据表,类似于关系型数据库的表结构。通过DataFrame能够处理一些非结构化的数据。DataSet则是RDD和DataFrame的结合体,用于处理更复杂的数据分析,如机器学习等。 在实现pyspark的大数据分析时,需要掌握一些重要的指令和API。常用的指令包括map、filter、reduce、flatMap等。这些指令能够帮助我们进行数据清洗、转换和过滤等操作。同时,pyspark还提供了一些高级的API如join、groupByKey、reduceByKey等。这些API可以用于处理和统计大量数据。 总之,pyspark的出现为我们提供了一种高效、便捷的方法来处理大数据。在实际的应用中,我们可以利用pyspark进行数据清洗、转换、过滤和计算等操作,实现数据的高效分析和处理。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值