多种人脸处理方案——人脸裁剪
文章目录
人脸裁剪处理方案
使用 OpenCV(集成 Haar、HOG 等检测算法)、dlib(68 点关键点定位)、MTCNN 库(深度学习检测)、mediapipe实现裁剪逻辑
Dlib 实现人脸裁剪
请确保已下载 shape_predictor_68_face_landmarks.dat 模型文件
1. 基于人脸边界框的裁剪(简单版)
加载模型:初始化 Dlib 的人脸检测器和关键点预测器。
检测人脸:在图像中定位人脸区域,获取边界框。
裁剪人脸:根据边界框坐标裁剪图像,可选择扩展范围。
2. 基于关键点的裁剪(精确版)
检测关键点:在人脸区域内检测 68 个关键点(如眼睛、鼻子、嘴巴等)。
计算裁剪范围:
根据关键点确定人脸轮廓的外接矩形。
基于双眼位置矫正人脸角度。
按美学比例调整上下边界(如头顶留 15%,下巴留 20%)。
裁剪并调整:旋转图像使人脸水平,按计算范围裁剪。
import dlib
import cv2
import numpy as np
from imutils import face_utils # 辅助工具库,需安装:pip install imutils
class 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_face
def 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 None
rotated_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_min
height = y_max - y_min
expand_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.bz2
cropper = 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 plt
class 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 None
rotated_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]) // 2
chin_y = rotated_keypoints['mouth_lower_lip'][1]
# 调整上边界,使眼睛位于图像上 1/3 处
target_eyes_y = (y2 - y1) // 3
current_eyes_y = eyes_center_y - y1
y1_adjust = current_eyes_y - target_eyes_y
y1 = max(0, y1 - y1_adjust)
# 调整下边界,使下巴底部位于图像下 1/4 处
target_chin_y = (y2 - y1) * 3 // 4
current_chin_y = chin_y - y1
y2_adjust = current_chin_y - target_chin_y
y2 = 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_face
def 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} 个人脸旋转后未能检测到")
continue
rotated_result = rotated_results[0]
rotated_box = rotated_result['box']
# 确定裁剪范围
x, y, width, height = rotated_box
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)))
# 裁剪人脸
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 的人脸裁剪(快速检测)
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_utils
def 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.shape
rgb_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 * h
nose_base = landmarks[164].y * h
chin = 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 | 高精度人脸检测与关键点定位 | 速度中等 | 人脸对齐、美颜应用 |
| Dlib | 68 点精细关键点定位 | 速度较慢,依赖模型文件 | 人脸分析、表情识别 |
| MediaPipe | 轻量高效,跨平台支持 | 关键点精度略低 | 实时视频处理、移动应用 |
| OpenCV | 速度快,简单易用 | 精度最低,依赖人工调整 | 快速原型、简单人脸检测 |
更多推荐


所有评论(0)