MTCNN 人脸识别:从不同场景照片中获取登记照(2)优化

ops/2024/10/25 16:17:50/

​ 对MTCNN 人脸识别:从不同场景照片中获取登记照(1)进行了优化:

优化修改说明

  1. 保持原始文件名处理

    • 删除了所有与重命名文件相关的代码,直接使用原始文件名进行处理和保存,确保支持中文文件名。
  2. 并行处理

    • 继续使用 ProcessPoolExecutor 进行多进程并行处理,提高处理效率。
    • 确保每个进程独立初始化 MTCNN,以避免进程间共享问题。
  3. 错误处理

    • 继续保留了对图像读取、关键点检测和裁剪过程中的错误处理,确保单个图像处理失败不会中断整个流程。

效果
在这里插入图片描述
说明:以上原始图片来至于互联网,若有侵权,联系删除!!!

环境创建请参考 MTCNN 人脸识别:从不同场景照片中获取登记照(1)

完整代码

"""
@File    : facial_recognition.py
@Author  : Bobo
@Blog    : https://blog.csdn.net/chinagaobo
@Note    : This code is for learning and communication purposes only
"""import os
import cv2
from PIL import Image
from mtcnn import MTCNN
import numpy as np
from concurrent.futures import ProcessPoolExecutor, as_completeddef read_image_pil(img_path):"""使用 PIL 读取图像,并转换为 RGB 格式的 NumPy 数组。"""try:with Image.open(img_path) as pil_img:pil_img = pil_img.convert('RGB')  # 确保图像为 RGB 模式img_rgb = np.array(pil_img)return img_rgbexcept Exception as e:print(f"无法使用 PIL 读取文件 {img_path},错误: {e}")return Nonedef process_image(img_path, output_dir, target_ratio=4 / 3, target_size=(460, 345)):"""处理单张图像:检测人脸、对齐、裁剪、调整尺寸,并保存结果。"""try:detector = MTCNN()# 读取图像(支持中文路径)img_rgb = read_image_pil(img_path)if img_rgb is None:return False# 在图像中检测人脸faces = detector.detect_faces(img_rgb)if not faces:print(f"未检测到人脸:{img_path}")return False# 处理检测到的第一个人脸face = faces[0]x, y, width, height = face['box']keypoints = face['keypoints']# 确保关键点存在if 'left_eye' not in keypoints or 'right_eye' not in keypoints:print(f"未检测到关键点(眼睛):{img_path}")return False# 对齐:使用两眼的位置计算旋转角度left_eye = keypoints['left_eye']right_eye = keypoints['right_eye']dx = right_eye[0] - left_eye[0]dy = right_eye[1] - left_eye[1]angle = np.degrees(np.arctan2(dy, dx))# 绕人脸中心旋转图像,使两眼水平center_x = x + width // 2center_y = y + height // 2center = (center_x, center_y)rotation_matrix = cv2.getRotationMatrix2D(center, angle, 1.0)rotated_img = cv2.warpAffine(img_rgb, rotation_matrix, (img_rgb.shape[1], img_rgb.shape[0]),flags=cv2.INTER_LINEAR)# 在旋转后的图像中重新检测人脸faces_rotated = detector.detect_faces(rotated_img)if not faces_rotated:print(f"旋转后未检测到人脸:{img_path}")return False# 使用重新检测到的人脸信息face_rot = faces_rotated[0]x_rot, y_rot, width_rot, height_rot = face_rot['box']# 计算人脸中心center_x_rot = x_rot + width_rot // 2center_y_rot = y_rot + height_rot // 2# 定义新的高度和宽度,维持目标长宽比new_height = int(height_rot * 1.5)new_width = int(new_height * target_ratio)# 计算裁剪区域,确保在图像边界内new_x1 = max(0, center_x_rot - new_width // 2)new_y1 = max(0, center_y_rot - new_height // 2)new_x2 = min(rotated_img.shape[1], center_x_rot + new_width // 2)new_y2 = min(rotated_img.shape[0], center_y_rot + new_height // 2)# 裁剪图像crop_image = rotated_img[new_y1:new_y2, new_x1:new_x2]# 调整裁剪后的图像尺寸adjusted_image = cv2.resize(crop_image, target_size, interpolation=cv2.INTER_AREA)# 从调整后的图像中裁剪左右两侧的50像素left_crop = 50right_crop = target_size[0] - 50if adjusted_image.shape[1] < right_crop:print(f"图像宽度不足以裁剪:{img_path}")return Falsecropped_final_image = adjusted_image[:, left_crop:right_crop]# 将最终裁剪后的图像转换为 PIL 格式pil_image = Image.fromarray(cropped_final_image)# 保存最终图像,保留原始文件名filename = os.path.basename(img_path)output_path = os.path.join(output_dir, filename)# 确保输出目录存在os.makedirs(os.path.dirname(output_path), exist_ok=True)pil_image.save(output_path)print(f"已保存裁剪并对齐后的图像至 '{output_path}'")return Trueexcept Exception as e:print(f"处理文件 {img_path} 时出错: {e}")return Falsedef main():# 定义输入和输出目录input_dir = "images"output_dir = "to_image"# 如果输出目录不存在则创建if not os.path.exists(output_dir):os.makedirs(output_dir)# 定义目标长宽比和目标尺寸target_ratio = 4 / 3target_size = (460, 345)  # 设计图像的目标尺寸# 支持的图像格式supported_formats = {'.png', '.jpg', '.jpeg', '.bmp', '.tiff'}# 获取输入目录中的文件列表,支持中文路径image_files = [os.path.join(input_dir, f) for f in os.listdir(input_dir)if os.path.splitext(f)[1].lower() in supported_formats]if not image_files:print("输入目录中没有支持的图像文件。")return# 使用 ProcessPoolExecutor 进行并行处理num_workers = os.cpu_count() or 4with ProcessPoolExecutor(max_workers=num_workers) as executor:# 提交所有任务futures = {executor.submit(process_image, img_path, output_dir, target_ratio, target_size): img_pathfor img_path in image_files}# 处理完成的任务for future in as_completed(futures):img_path = futures[future]try:result = future.result()if not result:print(f"处理失败: {img_path}")except Exception as exc:print(f"处理文件 {img_path} 时发生异常: {exc}")if __name__ == "__main__":main()

关键步骤说明

1. 读取图像

使用 PIL 读取图像,这样可以更好地支持包含中文字符的文件名。将图像转换为 RGB 模式,并转换为 NumPy 数组以供 MTCNN 处理。

def read_image_pil(img_path):"""使用 PIL 读取图像,并转换为 RGB 格式的 NumPy 数组。"""try:with Image.open(img_path) as pil_img:pil_img = pil_img.convert('RGB')  # 确保图像为 RGB 模式img_rgb = np.array(pil_img)return img_rgbexcept Exception as e:print(f"无法使用 PIL 读取文件 {img_path},错误: {e}")return None
2. 检测人脸并对齐

使用 MTCNN 检测人脸及其关键点(左眼和右眼)。根据两眼的位置计算旋转角度,使得两眼水平对齐。旋转图像后,重新检测人脸以确保对齐后的裁剪位置准确。

# 对齐:使用两眼的位置计算旋转角度
left_eye = keypoints['left_eye']
right_eye = keypoints['right_eye']
dx = right_eye[0] - left_eye[0]
dy = right_eye[1] - left_eye[1]
angle = np.degrees(np.arctan2(dy, dx))# 绕人脸中心旋转图像,使两眼水平
center_x = x + width // 2
center_y = y + height // 2
center = (center_x, center_y)
rotation_matrix = cv2.getRotationMatrix2D(center, angle, 1.0)
rotated_img = cv2.warpAffine(img_rgb, rotation_matrix, (img_rgb.shape[1], img_rgb.shape[0]), flags=cv2.INTER_LINEAR)# 在旋转后的图像中重新检测人脸
faces_rotated = detector.detect_faces(rotated_img)
if not faces_rotated:print(f"旋转后未检测到人脸:{img_path}")return False# 使用重新检测到的人脸信息
face_rot = faces_rotated[0]
x_rot, y_rot, width_rot, height_rot = face_rot['box']
3. 裁剪和调整尺寸

根据检测到的人脸位置,计算裁剪区域,保持预定义的长宽比。裁剪图像后,调整到目标尺寸,并从左右两侧各裁剪50像素,以保持统一的框架。

# 计算人脸中心
center_x_rot = x_rot + width_rot // 2
center_y_rot = y_rot + height_rot // 2# 定义新的高度和宽度,维持目标长宽比
new_height = int(height_rot * 1.5)
new_width = int(new_height * target_ratio)# 计算裁剪区域,确保在图像边界内
new_x1 = max(0, center_x_rot - new_width // 2)
new_y1 = max(0, center_y_rot - new_height // 2)
new_x2 = min(rotated_img.shape[1], center_x_rot + new_width // 2)
new_y2 = min(rotated_img.shape[0], center_y_rot + new_height // 2)# 裁剪图像
crop_image = rotated_img[new_y1:new_y2, new_x1:new_x2]# 调整裁剪后的图像尺寸
adjusted_image = cv2.resize(crop_image, target_size, interpolation=cv2.INTER_AREA)# 从调整后的图像中裁剪左右两侧的50像素
left_crop = 50
right_crop = target_size[0] - 50
if adjusted_image.shape[1] < right_crop:print(f"图像宽度不足以裁剪:{img_path}")return False
cropped_final_image = adjusted_image[:, left_crop:right_crop]
4. 保存处理后的图像

将裁剪和对齐后的图像转换为 PIL 格式,并保存到输出目录中,保留原始文件名。

# 将最终裁剪后的图像转换为 PIL 格式
pil_image = Image.fromarray(cropped_final_image)# 保存最终图像,保留原始文件名
filename = os.path.basename(img_path)
output_path = os.path.join(output_dir, filename)# 确保输出目录存在
os.makedirs(os.path.dirname(output_path), exist_ok=True)pil_image.save(output_path)
print(f"已保存裁剪并对齐后的图像至 '{output_path}'")

确保您的项目目录结构如下:

your_project/
├── images/          # 输入图像目录(包含需要处理的图像)
├── to_image/        # 输出图像目录(程序会自动创建)
├── facial_recognition.py       # 您的脚本文件(例如 facial_recognition.py)

运行脚本

将上述代码保存到您的脚本文件中(例如 facial_recognition.py),然后在终端或命令提示符中运行:

python face_recognition.py

运行后,程序将并行处理 images 目录中的所有支持格式(.png, .jpg, .jpeg, .bmp, .tiff)的图像,并将处理后的图像保存到 to_image 目录中。

总结

​ 通过以上修改,您可以高效地批量处理包含中文字符的图像文件,进行人脸检测、对齐和裁剪,并将处理后的图像保存到指定目录中,而无需进行背景移除操作。并行处理的使用大幅提高了处理效率,适合处理大量图像数据。如果大家有任何问题或建议,欢迎在评论区留言。


http://www.ppmy.cn/ops/128381.html

相关文章

草地杂草数据集野外草地数据集田间野草数据集YOLO格式VOC格式目标检测计算机视觉数据集

一、数据集概述 数据集名称&#xff1a;杂草图像数据集 数据集是一个包含野草种类的集合&#xff0c;其中每种野草都有详细的特征描述和标记。这些数据可以包括野草的图片、生长习性、叶片形状、颜色等特征。 1.1可能应用的领域 农业领域: 农业专家和农民可以利用这一数据集来…

docker 多架构接口数据交换

前言 docker 的仓库支持一个 tag 下多个架构镜像, 这是如何实现的呢? 抓包看看其数据交互流程 前提 错误处理 执行命令buildx报错: ERROR: Multi-platform build is not supported for the docker driver. Switch to a different driver, or turn on the containerd imag…

第 5 章 Kafka 消费者

5.1 Kafka 消费方式 5.2 Kafka 消费者工作流程 5.2.1 消费者总体工作流程 5.2.2 消费者组原理 5.2.3 消费者重要参数 5.3 消费者 API 5.3.1 独立消费者案例&#xff08;订阅主题&#xff09; package com.atguigu.kafka.consumer;import org.apache.kafka.clients.consume…

从零学习大模型(六)-----LoRA(上)

LoRA简介 LoRA&#xff08;Low-Rank Adaptation&#xff09;是一种参数高效的微调技术&#xff0c;旨在降低微调大规模预训练模型的存储和计算成本。**其核心思想是通过对模型的特定参数进行低秩分解&#xff0c;仅对少量附加参数进行训练&#xff0c;从而完成任务适应&#x…

证明在由特定矩阵生成的幺半子群中,存在收敛序列的子序列,其元素也能分别构成收敛序列

设 H H H是 G L 4 ( R ) GL_4(\mathbb{R}) GL4​(R)的由矩阵 ( 1 a 0 0 0 1 0 0 0 0 1 0 0 0 0 1 ) , ( 1 0 0 0 0 1 b 0 0 0 1 0 0 0 0 1 ) , ( 1 0 0 0 0 1 0 0 0 0 1 c 0 0 0 1 ) \begin{pmatrix}1&a&0&0\\ 0&1&0&0\\ 0&0&1&0\\ 0&…

Linux内核常见的网络丢包场景分析,零基础入门到精通,收藏这一篇就够了

摘要 一个数据包在网络中传输的过程中&#xff0c;是没法保证一定能被目的机接收到的。其中有各种各样的丢包原因&#xff0c;今天来学习一下数据包经过 linux 内核时常见的丢包场景。 1 收发包处理流程 有必要再回顾下 linux 内核的收发包处理流程&#xff0c;才能更好的认清…

pytorch nn.NLLLoss和nn.CrossEntropyLoss函数区别

nn.CrossEntropyLoss(交叉熵损失函数) 和nn.NLLLoss (负对数似然损失函数)的区别 输入格式&#xff1a; nn.CrossEntropyLoss&#xff1a;直接接受未归一化的 logits 作为输入&#xff0c;并在内部自动应用 log_softmax 来计算对数概率。nn.NLLLoss&#xff1a;接受对数概率&a…

【分布式微服务云原生】《Redis 分布式锁的挑战与解决方案及 RedLock 的强大魅力》

《Redis 分布式锁的挑战与解决方案及 RedLock 的强大魅力》 摘要&#xff1a; 本文深入探讨了使用 Redis 做分布式锁时可能遇到的各种问题&#xff0c;并详细阐述了相应的解决方案。同时&#xff0c;深入剖析了 RedLock 作为分布式锁的原因及原理&#xff0c;包括其多节点部署…