问小白 wenxiaobai
资讯
历史
科技
环境与自然
成长
游戏
财经
文学与艺术
美食
健康
家居
文化
情感
汽车
三农
军事
旅行
运动
教育
生活
星座命理

使用Dlib进行图像预处理:基于人脸关键点检测的图像裁剪

创作时间:
作者:
@小白创作中心

使用Dlib进行图像预处理:基于人脸关键点检测的图像裁剪

引用
CSDN
1.
https://blog.csdn.net/weixin_48981284/article/details/136592427

在深度学习中,图像预处理是一个非常重要的环节。特别是在处理人脸图像时,如何准确地定位人脸并进行标准化的裁剪,对于后续的模型训练和预测效果有着至关重要的影响。本文将详细介绍如何使用Dlib库进行人脸检测和图像预处理,帮助读者掌握这一实用技能。

众所周知,在将图像输入到神经网络前我们希望将图像预处理到同样的大小:对于小图像一般使用各种插值方法,而对于大的图像则一般使用各种裁剪方法。
使用 Dlib 模型在图像预处理阶段先找到人脸位置,在以该位置位置裁剪图片就可以保证裁剪出来的图片可以尽可能的保存原始图像中的人脸。

原始图像(经过压缩):

预处理后结果:

在使用前需要下载预训练的 Dlib 模型:

downloader.download_file("1xPmn19T6Bdd-_RfCVlgNBbfYoh1muYxR",   os.path.join(save_path, 'align.dat'))

首先先是相关设置:

from configs import paths_config            # 图片文件读取位置:
import dlib
import glob
import os
from tqdm import tqdm
from utils.alignment import align_face
"""
paths_config 中地址:
    path_config.dlib 为预训练的 Dlib 模型位置
    path_config.input_data_path 为预处理后图片保存的位置
"""
## 图片预处理调用部分
def pre_process_images(raw_images_path):
    """
    input: 
        raw_image_path: 原始图片文件位置 str
    output:
        None
    """
    ## 读取当前工作区间地址
    current_directory = os.getcwd()
    ## 默认处理后图片大小为 1024*1024
    IMAGE_SIZE = 1024
    ## 使用 dlip 包自带的人脸关键点检测图片中人脸位置、关键点 type(predictor) = dlib.shape_predictor object
    predictor = dlib.shape_predictor(paths_config.dlib)
    ## 移动工作区到图片位置
    os.chdir(raw_images_path)
    images_names = glob.glob(f'*')
    ## 
    aligned_images = []
    for image_name in tqdm(images_names):
        try:
            ## 通过获得的关键点裁剪图片
            aligned_image = align_face(filepath=f'{raw_images_path}/{image_name}',
                                       predictor=predictor,output_size=IMAGE_SIZE)
            ## 暂存在内存里
            aligned_images.append(aligned_image)
        ## 处理异常
        except Exception as e:
            print(e)
    ## 将调整好的图片保存到另一个文件夹里
    os.makedirs(paths_config.input_data_path, exist_ok=True)
    for image, name in zip(aligned_images, images_names):
        
        ## 去除图片后缀(.jpg .jpeg等)
        real_name = name.split('.')[0]
        ## 统一保存为 .jpeg 格式
        image.save(f'{paths_config.input_data_path}/{real_name}.jpeg')
    ## 回到最开始的工作区
    os.chdir(current_directory)
if __name__ == "__main__":
    pre_process_images('')

dlib.shape_predictor() 可以参考:python通过Dlib库实现人脸68点特征点标记_shape_predictor_68_face_landmarks.dat-CSDN博客
其中最主要的部分为:

aligned_image = align_face(filepath=f'{raw_images_path}/{image_name}',
                            predictor=predictor,output_size=IMAGE_SIZE)

输入的参数中 filepath 为原始图片保存位置;predictor 为 Dlib 模型获取到人脸关键位置信息;output_size 为希望裁剪后图像的大小 ( output_siaze * output_size ).
接下来转到具体函数文件:
调用包:

import numpy as np
import PIL
import PIL.Image
import scipy
import scipy.ndimage
import dlib

主要流程:

## 图片预处理主要过程
def align_face(filepath, predictor, output_size):
    """
    input:
        filepath 原始图片位置 str
        predictor 原始图像中人脸关键位置 dlib.shape_predictor object
        output_size 输出的预处理图像大小 int
    output:
        img 预处理后的图像 PIL.image
    """
    ## 获取图像人脸特征点
    lm = get_landmark(filepath, predictor)
    lm_chin = lm[0: 17]  # left-right 下颚 17 个点
    lm_eyebrow_left = lm[17: 22]  # left-right 左眉毛 5 个点
    lm_eyebrow_right = lm[22: 27]  # left-right 右眉毛 5 个点
    lm_nose = lm[27: 31]  # top-down 鼻子 4 个点
    lm_nostrils = lm[31: 36]  # top-down 鼻头 5 个点
    lm_eye_left = lm[36: 42]  # left-clockwise 左眼 6 个点
    lm_eye_right = lm[42: 48]  # left-clockwise 右眼 6 个点
    lm_mouth_outer = lm[48: 60]  # left-clockwise 上嘴唇 12 个点
    lm_mouth_inner = lm[60: 68]  # left-clockwise 下嘴唇 12 个点
    # Calculate auxiliary vectors. 计算各种向量
    eye_left = np.mean(lm_eye_left, axis=0)         # 计算左右眼中心点 (求平均值)   
    eye_right = np.mean(lm_eye_right, axis=0)
    eye_avg = (eye_left + eye_right) * 0.5
    eye_to_eye = eye_right - eye_left
    
    mouth_left = lm_mouth_outer[0]                  # 计算嘴巴的中心点
    mouth_right = lm_mouth_outer[6]
    mouth_avg = (mouth_left + mouth_right) * 0.5
    
    eye_to_mouth = mouth_avg - eye_avg              # 计算人脸的垂直方向
    # Choose oriented crop rectangle.
    x = eye_to_eye - np.flipud(eye_to_mouth) * [-1, 1]
    x /= np.hypot(*x)
    x *= max(np.hypot(*eye_to_eye) * 2.0, np.hypot(*eye_to_mouth) * 1.8)
    y = np.flipud(x) * [-1, 1]
    c = eye_avg + eye_to_mouth * 0.1
    quad = np.stack([c - x - y, c - x + y, c + x + y, c + x - y])   # 裁剪块的四个顶点
    qsize = np.hypot(*x) * 2                                        # 裁剪块大小
    # 读取原始图片
    img = PIL.Image.open(filepath)
    transform_size = output_size
    enable_padding = True
    ## 计算裁剪后图像大小
    shrink = int(np.floor(qsize / output_size * 0.5))
    if shrink > 1:
        rsize = (int(np.rint(float(img.size[0]) / shrink)), int(np.rint(float(img.size[1]) / shrink)))
        img = img.resize(rsize, PIL.Image.ANTIALIAS)
        quad /= shrink
        qsize /= shrink
    border = max(int(np.rint(qsize * 0.1)), 3)   # 计算边界值
    ## 计算裁剪部分
    crop = (int(np.floor(min(quad[:, 0]))), int(np.floor(min(quad[:, 1]))), int(np.ceil(max(quad[:, 0]))),
            int(np.ceil(max(quad[:, 1]))))
    crop = (max(crop[0] - border, 0), max(crop[1] - border, 0), min(crop[2] + border, img.size[0]),
            min(crop[3] + border, img.size[1]))
    if crop[2] - crop[0] < img.size[0] or crop[3] - crop[1] < img.size[1]:
        img = img.crop(crop)
        quad -= crop[0:2]
    # Pad.
    pad = (int(np.floor(min(quad[:, 0]))), int(np.floor(min(quad[:, 1]))), int(np.ceil(max(quad[:, 0]))),
           int(np.ceil(max(quad[:, 1]))))
    pad = (max(-pad[0] + border, 0), max(-pad[1] + border, 0), max(pad[2] - img.size[0] + border, 0),
           max(pad[3] - img.size[1] + border, 0))
    if enable_padding and max(pad) > border - 4:
        pad = np.maximum(pad, int(np.rint(qsize * 0.3)))
        img = np.pad(np.float32(img), ((pad[1], pad[3]), (pad[0], pad[2]), (0, 0)), 'reflect')
        h, w, _ = img.shape
        y, x, _ = np.ogrid[:h, :w, :1]
        mask = np.maximum(1.0 - np.minimum(np.float32(x) / pad[0], np.float32(w - 1 - x) / pad[2]),
                          1.0 - np.minimum(np.float32(y) / pad[1], np.float32(h - 1 - y) / pad[3]))
        blur = qsize * 0.02
        img += (scipy.ndimage.gaussian_filter(img, [blur, blur, 0]) - img) * np.clip(mask * 3.0 + 1.0, 0.0, 1.0)
        img += (np.median(img, axis=(0, 1)) - img) * np.clip(mask, 0.0, 1.0)
        img = PIL.Image.fromarray(np.uint8(np.clip(np.rint(img), 0, 255)), 'RGB')
        quad += pad[:2]
    # Transform.
    img = img.transform((transform_size, transform_size), PIL.Image.QUAD, (quad + 0.5).flatten(), PIL.Image.BILINEAR)
    if output_size < transform_size:
        img = img.resize((output_size, output_size), PIL.Image.ANTIALIAS)
    # 返回预处理后的图片
    return img

其中 get_landmark 函数为:

## 通过 blid 获取人物图像特征点
def get_landmark(filepath, predictor):
    """
    input:
        filepath 原始图片位置 str
        predictor 原始图像中人脸关键位置 dlib.shape_predictor object
    output:
        lm 包涵各个人脸关键点的列表 np.array shape=(68, 2)
    """
    
    detector = dlib.get_frontal_face_detector()
    img = dlib.load_rgb_image(filepath)
    
    ## 如果一张图片上有多个人脸,那么 dets 将会是个多元素列表
    dets = detector(img, 1)
    for k, d in enumerate(dets):
        shape = predictor(img, d)
    t = list(shape.parts())
    a = []
    for tt in t:
        a.append([tt.x, tt.y])
    lm = np.array(a)
    return lm
© 2023 北京元石科技有限公司 ◎ 京公网安备 11010802042949号