人脸裁剪处理方案

使用 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 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 的人脸裁剪(快速检测)

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_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 速度快,简单易用 精度最低,依赖人工调整 快速原型、简单人脸检测
Logo

中国智能体开发者社区,聚焦智能体与大模型开发,提供前沿资讯、实用工具链、开源项目及行业案例。通过技术沙龙、开发者大赛等活动,促进经验交流与协作,助力开发者快速构建创新智能应用。

更多推荐