当前位置: 首页 > news >正文

多种人脸处理方案——人脸裁剪

文章目录

  • 人脸裁剪处理方案
    • Dlib 实现人脸裁剪
    • MTCNN 人脸裁剪
    • 基于 [MediaPipe](https://chuoling.github.io/mediapipe/) Face Detection 的人脸裁剪(快速检测)
    • 基于 MediaPipe Face Mesh 的人脸裁剪(精细关键点)
    • 人脸处理方案对比

人脸裁剪处理方案

使用 OpenCV(集成 Haar、HOG 等检测算法)、dlib(68 点关键点定位)、MTCNN 库(深度学习检测)、mediapipe实现裁剪逻辑

Dlib 实现人脸裁剪

请确保已下载 shape_predictor_68_face_landmarks.dat 模型文件
在这里插入图片描述

人脸68、29、21、14、5关键点标注序号及对应关系

1. 基于人脸边界框的裁剪(简单版)
加载模型:初始化 Dlib 的人脸检测器和关键点预测器。
检测人脸:在图像中定位人脸区域,获取边界框。
裁剪人脸:根据边界框坐标裁剪图像,可选择扩展范围。
2. 基于关键点的裁剪(精确版)
检测关键点:在人脸区域内检测 68 个关键点(如眼睛、鼻子、嘴巴等)。
计算裁剪范围:
根据关键点确定人脸轮廓的外接矩形。
基于双眼位置矫正人脸角度。
按美学比例调整上下边界(如头顶留 15%,下巴留 20%)。
裁剪并调整:旋转图像使人脸水平,按计算范围裁剪。

import dlib
import cv2
import numpy as np
from imutils import face_utils  # 辅助工具库,需安装:pip install imutilsclass DlibFaceCropper:def __init__(self, predictor_path="shape_predictor_68_face_landmarks.dat"):"""初始化 Dlib 人脸检测器和关键点预测器"""# 人脸检测器self.detector = dlib.get_frontal_face_detector()# 关键点预测器(需提前下载模型文件)self.predictor = dlib.shape_predictor(predictor_path)def crop_by_bounding_box(self, image_path, output_path, expand_ratio=0.2):"""基于人脸边界框裁剪(简单版)expand_ratio: 边界框扩展比例,避免裁剪过紧"""# 读取图像image = cv2.imread(image_path)if image is None:raise ValueError(f"无法读取图像: {image_path}")# 转换为灰度图(人脸检测更快)gray = cv2.cvtColor(image, cv2.COLOR_BGR2GRAY)# 检测人脸faces = self.detector(gray, 1)  # 第二个参数为向上采样次数,增加可提高小人脸检测率if len(faces) == 0:print("未检测到人脸")return None# 处理第一个检测到的人脸(可扩展为多人脸)face = faces[0]# 扩展边界框h, w = image.shape[:2]x, y, width, height = face.left(), face.top(), face.width(), face.height()# 计算扩展后的边界expand_w = int(width * expand_ratio)expand_h = int(height * expand_ratio)x1 = max(0, x - expand_w)y1 = max(0, y - expand_h)x2 = min(w, x + width + expand_w)y2 = min(h, y + height + expand_h)# 裁剪人脸cropped_face = image[y1:y2, x1:x2]# 保存结果cv2.imwrite(output_path, cropped_face)print(f"人脸裁剪完成,保存至: {output_path}")return cropped_facedef crop_by_landmarks(self, image_path, output_path, expand_ratio=0.15, desired_face_width=256):"""基于关键点的人脸裁剪(精确版)expand_ratio: 基于关键点计算的边界框扩展比例desired_face_width: 输出人脸图像的目标宽度(等比例缩放)"""# 读取图像image = cv2.imread(image_path)if image is None:raise ValueError(f"无法读取图像: {image_path}")# 转换为灰度图gray = cv2.cvtColor(image, cv2.COLOR_BGR2GRAY)# 检测人脸faces = self.detector(gray, 1)if len(faces) == 0:print("未检测到人脸")return None# 处理第一个人脸face = faces[0]# 检测关键点shape = self.predictor(gray, face)shape = face_utils.shape_to_np(shape)  # 转换为 NumPy 数组# 1. 基于关键点计算人脸轮廓的边界框# 人脸轮廓点索引:0-16(下巴),17-26(眉毛),27-35(鼻子),36-47(眼睛),48-67(嘴巴)face_points = np.vstack([shape[0:17],  # 下巴轮廓shape[17:27],  # 眉毛shape[27:36]]) # 鼻子# 计算边界框x_min, y_min = np.min(face_points, axis=0)x_max, y_max = np.max(face_points, axis=0)# 2. 基于双眼位置计算旋转角度(使双眼水平)left_eye = shape[36:42]  # 左眼关键点right_eye = shape[42:48] # 右眼关键点# 计算双眼中心left_eye_center = np.mean(left_eye, axis=0).astype(int)right_eye_center = np.mean(right_eye, axis=0).astype(int)# 计算旋转角度dy = right_eye_center[1] - left_eye_center[1]dx = right_eye_center[0] - left_eye_center[0]angle = np.degrees(np.arctan2(dy, dx))# 3. 旋转图像使人脸水平h, w = image.shape[:2]center = (w // 2, h // 2)M = cv2.getRotationMatrix2D(center, angle, 1.0)rotated = cv2.warpAffine(image, M, (w, h))# 4. 旋转后的关键点坐标也需要相应变换# 简化处理:重新检测旋转后的人脸和关键点rotated_gray = cv2.cvtColor(rotated, cv2.COLOR_BGR2GRAY)rotated_faces = self.detector(rotated_gray, 1)if len(rotated_faces) == 0:print("旋转后未能检测到人脸")return Nonerotated_face = rotated_faces[0]rotated_shape = self.predictor(rotated_gray, rotated_face)rotated_shape = face_utils.shape_to_np(rotated_shape)# 重新计算旋转后的人脸边界框face_points = np.vstack([rotated_shape[0:17], rotated_shape[17:27], rotated_shape[27:36]])x_min, y_min = np.min(face_points, axis=0)x_max, y_max = np.max(face_points, axis=0)# 扩展边界框width = x_max - x_minheight = y_max - y_minexpand_w = int(width * expand_ratio)expand_h = int(height * expand_ratio)# 确保边界不超出图像范围x1 = max(0, x_min - expand_w)y1 = max(0, y_min - expand_h)x2 = min(w, x_max + expand_w)y2 = min(h, y_max + expand_h)# 5. 裁剪人脸cropped_face = rotated[y1:y2, x1:x2]# 6. 调整为目标尺寸(可选)if desired_face_width is not None:h, w = cropped_face.shape[:2]ratio = desired_face_width / max(h, w)cropped_face = cv2.resize(cropped_face, (int(w * ratio), int(h * ratio)))# 保存结果cv2.imwrite(output_path, cropped_face)print(f"基于关键点的人脸裁剪完成,保存至: {output_path}")return cropped_face# 使用示例
if __name__ == "__main__":# 请确保已下载 shape_predictor_68_face_landmarks.dat 模型文件# 下载地址:http://dlib.net/files/shape_predictor_68_face_landmarks.dat.bz2cropper = DlibFaceCropper(predictor_path="shape_predictor_68_face_landmarks.dat")# 简单版裁剪(基于边界框)cropper.crop_by_bounding_box(image_path="input.jpg",output_path="cropped_by_box.jpg",expand_ratio=0.2  # 边界框扩展 20%)# 精确版裁剪(基于关键点)cropper.crop_by_landmarks(image_path="input.jpg",output_path="cropped_by_landmarks.jpg",expand_ratio=0.15,  # 基于关键点扩展 15%desired_face_width=300  # 输出宽度为 300 像素)

MTCNN 人脸裁剪

模型初始化:加载预训练的 MTCNN 模型。
人脸检测:在图像中定位人脸,获取边界框和关键点坐标。
角度矫正:基于双眼位置计算旋转角度,矫正人脸水平。
裁剪范围确定:根据关键点扩展裁剪区域(保留头发、肩部等)。
确保裁剪比例符合美学标准(如头顶留白 15%,下巴留白 20%)。
裁剪与输出:旋转图像并按计算范围裁剪。

import cv2
import numpy as np
from mtcnn import MTCNN
import matplotlib.pyplot as pltclass MTCNNFaceCropper:def __init__(self):"""初始化 MTCNN 检测器"""self.detector = MTCNN()def crop_face(self, image_path, output_path, margin=0.2, desired_size=256, save=True):"""裁剪人脸并进行角度矫正参数:image_path: 输入图像路径output_path: 输出图像路径margin: 人脸周围的留白比例desired_size: 输出人脸的目标尺寸save: 是否保存结果"""# 读取图像image = cv2.imread(image_path)if image is None:raise ValueError(f"无法读取图像: {image_path}")# 转换为 RGB(MTCNN 要求输入为 RGB)rgb_image = cv2.cvtColor(image, cv2.COLOR_BGR2RGB)# 检测人脸results = self.detector.detect_faces(rgb_image)if not results:print("未检测到人脸")return None# 获取第一个检测结果(可扩展为多人脸处理)result = results[0]bounding_box = result['box']keypoints = result['keypoints']# 1. 计算旋转角度(基于双眼位置)left_eye = keypoints['left_eye']right_eye = keypoints['right_eye']# 计算双眼连线的角度dy = right_eye[1] - left_eye[1]dx = right_eye[0] - left_eye[0]angle = np.degrees(np.arctan2(dy, dx))# 2. 旋转图像使人脸水平h, w = image.shape[:2]center = (w // 2, h // 2)M = cv2.getRotationMatrix2D(center, angle, 1.0)rotated = cv2.warpAffine(image, M, (w, h))# 3. 旋转后的关键点也需要相应变换# 简化处理:重新检测旋转后的人脸rotated_rgb = cv2.cvtColor(rotated, cv2.COLOR_BGR2RGB)rotated_results = self.detector.detect_faces(rotated_rgb)if not rotated_results:print("旋转后未能检测到人脸")return Nonerotated_result = rotated_results[0]rotated_box = rotated_result['box']rotated_keypoints = rotated_result['keypoints']# 4. 确定裁剪范围x, y, width, height = rotated_box# 计算扩展后的边界(margin 参数控制扩展比例)x1 = max(0, int(x - width * margin))y1 = max(0, int(y - height * margin))x2 = min(w, int(x + width * (1 + margin)))y2 = min(h, int(y + height * (1 + margin)))# 5. 基于关键点进一步优化裁剪范围(可选)# 例如:确保眼睛位于图像上 1/3 处,下巴底部位于图像下 1/4 处eyes_center_y = (rotated_keypoints['left_eye'][1] + rotated_keypoints['right_eye'][1]) // 2chin_y = rotated_keypoints['mouth_lower_lip'][1]# 调整上边界,使眼睛位于图像上 1/3 处target_eyes_y = (y2 - y1) // 3current_eyes_y = eyes_center_y - y1y1_adjust = current_eyes_y - target_eyes_yy1 = max(0, y1 - y1_adjust)# 调整下边界,使下巴底部位于图像下 1/4 处target_chin_y = (y2 - y1) * 3 // 4current_chin_y = chin_y - y1y2_adjust = current_chin_y - target_chin_yy2 = min(h, y2 + y2_adjust)# 6. 裁剪人脸cropped_face = rotated[y1:y2, x1:x2]# 7. 调整为目标尺寸if desired_size is not None:cropped_face = cv2.resize(cropped_face, (desired_size, desired_size))# 8. 保存结果if save:cv2.imwrite(output_path, cropped_face)print(f"人脸裁剪完成,保存至: {output_path}")return cropped_facedef crop_multiple_faces(self, image_path, output_dir, margin=0.2, desired_size=256):"""裁剪图像中的多个人脸"""import os# 读取图像image = cv2.imread(image_path)if image is None:raise ValueError(f"无法读取图像: {image_path}")rgb_image = cv2.cvtColor(image, cv2.COLOR_BGR2RGB)# 检测人脸results = self.detector.detect_faces(rgb_image)if not results:print("未检测到人脸")return []cropped_faces = []for i, result in enumerate(results):# 获取边界框和关键点bounding_box = result['box']keypoints = result['keypoints']# 计算旋转角度left_eye = keypoints['left_eye']right_eye = keypoints['right_eye']dy = right_eye[1] - left_eye[1]dx = right_eye[0] - left_eye[0]angle = np.degrees(np.arctan2(dy, dx))# 旋转图像h, w = image.shape[:2]center = (w // 2, h // 2)M = cv2.getRotationMatrix2D(center, angle, 1.0)rotated = cv2.warpAffine(image, M, (w, h))# 重新检测旋转后的人脸rotated_rgb = cv2.cvtColor(rotated, cv2.COLOR_BGR2RGB)rotated_results = self.detector.detect_faces(rotated_rgb)if not rotated_results:print(f"第 {i+1} 个人脸旋转后未能检测到")continuerotated_result = rotated_results[0]rotated_box = rotated_result['box']# 确定裁剪范围x, y, width, height = rotated_boxx1 = max(0, int(x - width * margin))y1 = max(0, int(y - height * margin))x2 = min(w, int(x + width * (1 + margin)))y2 = min(h, int(y + height * (1 + margin)))# 裁剪人脸cropped_face = rotated[y1:y2, x1:x2]# 调整尺寸if desired_size is not None:cropped_face = cv2.resize(cropped_face, (desired_size, desired_size))# 保存结果output_path = os.path.join(output_dir, f"face_{i+1}.jpg")cv2.imwrite(output_path, cropped_face)print(f"第 {i+1} 个人脸裁剪完成,保存至: {output_path}")cropped_faces.append(cropped_face)return cropped_faces# 使用示例
if __name__ == "__main__":# 初始化裁剪器cropper = MTCNNFaceCropper()# 裁剪单个人脸cropper.crop_face(image_path="input.jpg",output_path="cropped_face.jpg",margin=0.3,  # 人脸周围留白 30%desired_size=300)# 裁剪多个人脸(可选)# cropper.crop_multiple_faces(#     image_path="group_photo.jpg",#     output_dir="faces/",#     margin=0.25# )

基于 MediaPipe Face Detection 的人脸裁剪(快速检测)

468关键点原图
在这里插入图片描述

MediaPipe 集成人脸识别,人体姿态评估,人手检测模型
Mediapipe入门——搭建姿态检测模型并实时输出人体关节点3d坐标(2024.1.4更新)

核心逻辑
MediaPipe Face Detection 模型专注于快速定位人脸区域,输出人脸边界框(包含置信度),适合对速度要求高、精度要求适中的场景(如实时裁剪、批量处理)。
初始化模型:加载 MediaPipe 人脸检测模型,配置检测参数(如最小置信度)。
处理图像:将输入图像转换为模型所需格式(如 RGB),传入模型检测人脸。
提取边界框:从检测结果中获取人脸边界框坐标(归一化坐标需转换为像素坐标)。
裁剪与调整:根据边界框裁剪人脸,可按需扩展范围(避免裁剪过紧)。
输出结果:保存或返回裁剪后的人脸图像。

import cv2
import mediapipe as mp# 初始化 MediaPipe 人脸检测
mp_face_detection = mp.solutions.face_detection
mp_drawing = mp.solutions.drawing_utils  # 用于可视化(可选)def crop_face_with_detection(input_image_path, output_image_path, expand_ratio=0.1):# 读取图像image = cv2.imread(input_image_path)if image is None:raise ValueError("无法读取输入图像")h, w, _ = image.shape  # 获取图像高、宽# 加载人脸检测模型with mp_face_detection.FaceDetection(model_selection=1,  # 0 适用于近景(2米内),1 适用于远景(5米内)min_detection_confidence=0.5  # 最小置信度阈值) as face_detection:# 转换为 RGB(MediaPipe 要求输入为 RGB)results = face_detection.process(cv2.cvtColor(image, cv2.COLOR_BGR2RGB))if not results.detections:print("未检测到人脸")return# 处理第一个检测到的人脸(可扩展为多个人脸)detection = results.detections[0]bbox = detection.location_data.relative_bounding_box# 转换归一化坐标为像素坐标xmin = int(bbox.xmin * w)ymin = int(bbox.ymin * h)width = int(bbox.width * w)height = int(bbox.height * h)# 扩展裁剪范围(避免裁剪过紧,如上下左右各扩展 10%)expand_w = int(width * expand_ratio)expand_h = int(height * expand_ratio)x1 = max(0, xmin - expand_w)y1 = max(0, ymin - expand_h)x2 = min(w, xmin + width + expand_w)y2 = min(h, ymin + height + expand_h)# 裁剪人脸cropped_face = image[y1:y2, x1:x2]# 保存结果cv2.imwrite(output_image_path, cropped_face)print(f"裁剪完成,保存至 {output_image_path}")# 调用示例
crop_face_with_detection(input_image_path="input.jpg",output_image_path="cropped_detection.jpg",expand_ratio=0.15  # 扩展 15% 范围,保留更多头发/肩部
)

基于 MediaPipe Face Mesh 的人脸裁剪(精细关键点)

核心逻辑
MediaPipe Face Mesh 可检测 468 个人脸关键点(包括眼睛、鼻子、嘴唇、轮廓等),适合需要高精度调整的场景(如按五官比例裁剪、矫正人脸角度)。
初始化模型:加载 Face Mesh 模型,配置参数(如最小检测 / 跟踪置信度)。
检测关键点:获取 468 个关键点的像素坐标,筛选关键区域(如轮廓、双眼)。
确定裁剪范围:用轮廓关键点计算人脸外接矩形(更精准的边界)。
基于双眼坐标矫正人脸角度(确保水平)。
按美学比例(如 “三庭”)调整上下边界(如头顶留 10%,下巴底部留 20%)。
裁剪与输出:根据计算的范围裁剪,并保存结果。

import cv2
import mediapipe as mp
import numpy as np# 初始化 MediaPipe Face Mesh
mp_face_mesh = mp.solutions.face_mesh
mp_drawing = mp.solutions.drawing_utilsdef crop_face_with_mesh(input_image_path, output_image_path):image = cv2.imread(input_image_path)if image is None:raise ValueError("无法读取输入图像")h, w, _ = image.shapergb_image = cv2.cvtColor(image, cv2.COLOR_BGR2RGB)# 加载 Face Mesh 模型with mp_face_mesh.FaceMesh(static_image_mode=True,  # 静态图像模式(非视频)max_num_faces=1,  # 最多检测 1 个人脸min_detection_confidence=0.5) as face_mesh:results = face_mesh.process(rgb_image)if not results.multi_face_landmarks:print("未检测到人脸关键点")return# 获取第一个人脸的关键点(468 个)landmarks = results.multi_face_landmarks[0].landmark# 1. 提取轮廓关键点(用于计算人脸边界)# 轮廓关键点索引范围:0-16(下巴)、68-81(鬓角/头顶)等(可根据需求调整)contour_indices = list(range(0, 17)) + list(range(68, 82))contour_points = []for idx in contour_indices:x = int(landmarks[idx].x * w)y = int(landmarks[idx].y * h)contour_points.append((x, y))# 计算轮廓的最小外接矩形(比检测框更精准)x_coords = [p[0] for p in contour_points]y_coords = [p[1] for p in contour_points]xmin, xmax = min(x_coords), max(x_coords)ymin, ymax = min(y_coords), max(y_coords)# 2. 基于双眼关键点矫正水平角度(可选)# 左眼中心(关键点 362)和右眼中心(关键点 385)left_eye = landmarks[362]right_eye = landmarks[385]left_eye_xy = (left_eye.x * w, left_eye.y * h)right_eye_xy = (right_eye.x * w, right_eye.y * h)# 计算双眼连线与水平线的夹角dx = right_eye_xy[0] - left_eye_xy[0]dy = right_eye_xy[1] - left_eye_xy[1]angle = np.degrees(np.arctan2(dy, dx))  # 角度(若为负则向左倾斜)# 旋转图像使双眼水平(可选步骤,根据需求开启)if abs(angle) > 5:  # 角度超过 5° 则矫正M = cv2.getRotationMatrix2D((w//2, h//2), angle, 1)image = cv2.warpAffine(image, M, (w, h))# 重新计算旋转后的关键点(简化处理:直接用旋转后的图像重新检测,或通过矩阵转换坐标)# 此处为简化,假设旋转后边界框不变(实际应用需重新计算)# 3. 按美学比例调整裁剪范围(三庭:发际线到眉弓=眉弓到鼻底=鼻底到下巴)# 眉弓参考点(关键点 6)、鼻底(关键点 164)、下巴尖(关键点 152)brow = landmarks[6].y * hnose_base = landmarks[164].y * hchin = landmarks[152].y * h# 上庭:头顶到眉弓,下庭:鼻底到下巴,取最大距离作为上下扩展基准upper_face = brow - ymin  # 头顶到眉弓lower_face = ymax - nose_base  # 鼻底到下巴expand_up = max(0, int(upper_face * 0.1))  # 头顶额外留 10%expand_down = max(0, int(lower_face * 0.2))  # 下巴额外留 20%# 最终裁剪范围x1 = max(0, xmin - 20)  # 左右各扩展 20 像素(可调整)y1 = max(0, ymin - expand_up)x2 = min(w, xmax + 20)y2 = min(h, ymax + expand_down)# 裁剪并保存cropped_face = image[y1:y2, x1:x2]cv2.imwrite(output_image_path, cropped_face)print(f"基于关键点裁剪完成,保存至 {output_image_path}")# 调用示例
crop_face_with_mesh(input_image_path="input.jpg",output_image_path="cropped_mesh.jpg"
)

人脸处理方案对比

方案优势劣势适用场景
MTCNN高精度人脸检测与关键点定位速度中等人脸对齐、美颜应用
Dlib68 点精细关键点定位速度较慢,依赖模型文件人脸分析、表情识别
MediaPipe轻量高效,跨平台支持关键点精度略低实时视频处理、移动应用
OpenCV速度快,简单易用精度最低,依赖人工调整快速原型、简单人脸检测
http://www.dtcms.com/a/275026.html

相关文章:

  • Webview 中可用的 VS Code 方法
  • G1 垃圾回收算法详解
  • 【TCP/IP】16. 简单网络管理协议
  • 天晟科技携手万表平台,共同推动RWA项目发展
  • 从「小公司人事」到「HRBP」:选对工具,比转岗更能解决成长焦虑
  • Java大厂面试故事:谢飞机的互联网音视频场景技术面试全纪录(Spring Boot、MyBatis、Kafka、Redis、AI等)
  • kubernetes单机部署踩坑笔记
  • DIDCTF-蓝帽杯
  • 谷歌云代理商:谷歌云TPU/GPU如何加速您的AI模型训练和推理
  • 【数据结构与算法】206.反转链表(LeetCode)
  • C++:非类型模板参数,模板特化以及模板的分离编译
  • 实现将文本数据(input_text)转换为input_embeddings的操作
  • 《从依赖纠缠到接口协作:ASP.NET Core注入式开发指南》
  • Vue 表单开发优化实践:如何优雅地合并 `data()` 与 `resetForm()` 中的重复对象
  • Sigma-Aldrich 细胞培养实验方案 | 通过Hoechst DNA染色检测细胞的支原体污染
  • 拔高原理篇
  • 奇哥面试记:SpringBoot整合RabbitMQ与高级特性,一不小心吊打面试官
  • java底层的native和沙箱安全机制
  • Lecture #19 : Multi-Version Concurrency Control
  • 深入理解JVM的垃圾收集(GC)机制
  • Next知识框架、SSR、SSG和ISR知识框架梳理
  • c++——运算符的重载
  • 鸿蒙开发之ArkTS常量与变量的命名规则
  • 面向对象编程
  • [面试] 手写题-选择排序
  • 持有对象-泛型和类型安全的容器
  • 深度学习中的归一化技术详解:BN、LN、IN、GN
  • Kubernetes 高级调度特性
  • C语言:位运算
  • Redis 哨兵机制