使用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
热门推荐
用生涯心理学助力孩子找到人生目标
《西游记》里的谐音梗,你get到了吗?
《西游记》诗词:你最爱哪一句?
表达的规则——六个言之,不是六个核桃
香港散步地图:这几条Citywalk路线,带你漫步千面港岛
双十一揭秘:孙先生教你用八字看婚姻运势
石家庄周边自驾游:天长古镇vs红崖谷,你更爱哪一个?
秋日白石山自驾摄影攻略:捕捉最美红桦林
中央管理企业与地方国企薪酬制度差异有哪些
轻松办理食品经营许可证:网上申请的详细图解指南
2024年最新新手必看:食品生产许可证办理经验
Windows 10蓝牙无法打开?这份超详细解决方案请收好!
Windows 10蓝牙又双叒叕出问题?这些技巧让你秒变高手!
打印机蓝牙故障?这些小妙招让你秒变高手!
蓝牙适配器换新后连不上设备?教你一招搞定!
欧协联附加赛前瞻:根特VS皇家贝蒂斯—元神安东尼的再次启动
基本面分析:如何挑选优质股?
港大商学院首届菁英夏令营攻略|6月提前锁定26fall offer!
.NET开发者的救星:轻松搞定System.Windows.Forms引用难题!
.NET开发必备:System.Windows.Forms引用添加全攻略
甘薯trifida自交亲和性背后的秘密
已故《老友记》明星马修·佩里的X账户被加密货币骗子入侵
细数接种卡介苗后的不良反应,红肿、乏力均属正常表现
卡介苗接种时间
定期做儿童保健,为孩子的健康保驾护航!
“新疆”之名从何而来?
考研党必看!饮食+运动助你上岸
考研成功之道:心态调整与科学备考双管齐下
考研复试如何用模拟考试预测上岸?
动脉粥样硬化患者血脂控制,饮食结构调整建议,定期检查项目安排