使用Python做的一个简单的人脸识别项目

使用Python做的一个简单的人脸识别项目

最近在学习使用使用python来学习OpenCV,验证其相关的算法。因为平时接触到人脸打卡,于是想着能否用Python和OpenCV来实现一个简单的人脸识别项目。代码由豆包生成。

一、知识点概述

人脸识别是计算机视觉的经典应用场景,OpenCV 作为开源的计算机视觉库,提供了成熟的人脸检测与识别工具链。本次笔记聚焦于基于 OpenCV 的人脸检测(Haar 级联分类器) + 人脸识别(LBPH 算法) 完整实现,从原理、步骤到代码全覆盖,适合入门学习与实战。

核心概念

  1. 人脸检测:定位图像中人脸的位置(坐标 + 尺寸),是识别的前提;
  2. 人脸识别:将检测到的人脸与已知人脸库匹配,确定身份;
  3. ROI(感兴趣区域):截取人脸区域单独处理,提升效率与精度。

二、核心原理说明

1. 人脸检测:Haar 级联分类器

  • 原理:基于 Haar 特征(边缘、纹理、对比度等),通过 Adaboost 算法筛选出关键特征,再用级联分类器快速排除非人脸区域,最终定位人脸。
  • 优势:速度快、无需训练,OpenCV 内置预训练模型(haarcascade_frontalface_default.xml),开箱即用;
  • 适用场景:正面人脸检测(对侧脸 / 遮挡鲁棒性较弱)。

2. 人脸识别:LBPH(局部二值模式直方图)算法

  • 原理
    1. 对人脸灰度图提取 LBP 特征(将每个像素与周围像素比较,生成二进制编码);
    2. 将人脸分块,统计每块的 LBP 特征直方图,拼接成整个人脸的特征向量;
    3. 计算待识别人脸与已知人脸的特征向量相似度,匹配身份。
  • 优势
    • 对光照变化、小角度旋转鲁棒;
    • 无需大量样本,单张人脸即可训练;
    • 支持动态更新人脸库。

三、详细操作步骤

exported_image

步骤 1:环境准备

  1. 安装 OpenCV:

    本人已经通过清华的镜像安装了如下库:使用的python解释器版本是python3.12

    ScreenShot_2026-03-22_135300_791

清华镜像地址:清华镜像地址

全局永久配置(所有项目生效)的操作是:

在系统终端或 PyCharm Terminal 执行

pip config set global.index-url https://pypi.tuna.tsinghua.edu.cn/simple
pip config set global.trusted-host pypi.tuna.tsinghua.edu.cn

步骤2:项目结构

face_recognition_project/  # 项目根目录
├─ data/                   # 数据文件夹
│  ├─ raw_faces/           # 原始人脸图片(手动放)
│  │  ├─ zhangsan/         # 张三的人脸图片(5-10张)
│  │  └─ lisi/             # 李四的人脸图片(5-10张)
│  └─ processed_faces/     # 自动生成的人脸ROI(运行代码后生成)
├─ model/                  # 模型文件夹(自动生成)
│  ├─ face_recognizer.yml  # 训练好的LBPH模型
│  └─ label_map.npy        # 标签映射文件
├─ modules/                # 功能模块文件夹(放拆分的代码)
│  ├─ data_preprocess.py   # 人脸ROI提取代码
│  ├─ model_train.py       # 模型训练代码
│  └─ realtime_recognize.py# 实时识别代码
└─ main.py                 # 主运行文件(核心入口)

如图所示:

ScreenShot_2026-03-22_135950_513

2.1 拆分代码到对应模块

1.modules/data_preprocess.py(人脸 ROI 提取)

import cv2
import os
import numpy as np

# 核心修改:获取项目根目录的绝对路径(适配main.py的位置)
# 因为data_preprocess.py在modules里,所以要回退一级到项目根目录
BASE_DIR = os.path.dirname(os.path.dirname(os.path.abspath(__file__)))
# 绝对路径配置(再也不会错!)
RAW_FACE_PATH = os.path.join(BASE_DIR, "data", "raw_faces")
PROCESSED_FACE_PATH = os.path.join(BASE_DIR, "data", "processed_faces")

# 确保文件夹存在(即使没创建,代码也会自动建)
os.makedirs(PROCESSED_FACE_PATH, exist_ok=True)
os.makedirs(RAW_FACE_PATH, exist_ok=True)  # 新增:自动创建raw_faces

# 加载人脸检测器
face_cascade = cv2.CascadeClassifier(
    cv2.data.haarcascades + "haarcascade_frontalface_default.xml"
)


def extract_face_roi(img_path, save_path, person_name):
    """提取单张图片的人脸ROI"""
    img = cv2.imread(img_path)
    if img is None:
        print(f"跳过无效图片:{img_path}")
        return

    gray = cv2.cvtColor(img, cv2.COLOR_BGR2GRAY)
    faces = face_cascade.detectMultiScale(
        gray, scaleFactor=1.1, minNeighbors=5, minSize=(30, 30)
    )

    for i, (x, y, w, h) in enumerate(faces):
        face_roi = gray[y:y + h, x:x + w]
        face_roi = cv2.resize(face_roi, (100, 100))  # 统一尺寸
        roi_save_path = os.path.join(save_path, f"{person_name}_{i}.jpg")
        cv2.imwrite(roi_save_path, face_roi)
        print(f"保存ROI:{roi_save_path}")


def batch_extract():
    """批量处理原始人脸数据"""
    # 先检查raw_faces是否为空
    if not os.listdir(RAW_FACE_PATH):
        print("警告:data/raw_faces 文件夹为空!请先放入人脸图片文件夹(如zhangsan/)")
        return

    for person_dir in os.listdir(RAW_FACE_PATH):
        person_raw_path = os.path.join(RAW_FACE_PATH, person_dir)
        if not os.path.isdir(person_raw_path):
            continue

        # 创建人物的ROI保存目录
        person_processed_path = os.path.join(PROCESSED_FACE_PATH, person_dir)
        os.makedirs(person_processed_path, exist_ok=True)

        # 处理该人物的所有图片
        for img_name in os.listdir(person_raw_path):
            img_path = os.path.join(person_raw_path, img_name)
            extract_face_roi(img_path, person_processed_path, person_dir)


# 测试用(单独运行该文件时执行)
if __name__ == "__main__":
    batch_extract()
    print("ROI提取完成!")

2.modules/model_train.py(模型训练)

import cv2
import os
import numpy as np

# 核心修改:绝对路径配置
BASE_DIR = os.path.dirname(os.path.dirname(os.path.abspath(__file__)))
PROCESSED_FACE_PATH = os.path.join(BASE_DIR, "data", "processed_faces")
MODEL_SAVE_PATH = os.path.join(BASE_DIR, "model", "face_recognizer.yml")
LABEL_MAP_PATH = os.path.join(BASE_DIR, "model", "label_map.npy")


def train_model():
    """训练LBPH人脸识别模型"""
    # 检查processed_faces是否有数据
    if not os.path.exists(PROCESSED_FACE_PATH) or not os.listdir(PROCESSED_FACE_PATH):
        print("警告:没有提取到人脸ROI!请先运行步骤1提取ROI")
        return

    faces = []
    labels = []
    label_map = {}
    current_label = 0

    # 加载处理后的人脸数据
    for person_dir in os.listdir(PROCESSED_FACE_PATH):
        person_path = os.path.join(PROCESSED_FACE_PATH, person_dir)
        if not os.path.isdir(person_path):
            continue

        label_map[person_dir] = current_label  # 人名→数字标签
        for img_name in os.listdir(person_path):
            img_path = os.path.join(person_path, img_name)
            face = cv2.imread(img_path, 0)  # 灰度图读取
            faces.append(face)
            labels.append(current_label)
        current_label += 1

    # 转换为numpy数组
    faces = np.array(faces)
    labels = np.array(labels)

    # 初始化并训练LBPH识别器
    recognizer = cv2.face.LBPHFaceRecognizer_create(
        radius=1, neighbors=8, grid_x=8, grid_y=8
    )
    recognizer.train(faces, labels)

    # 保存模型和标签映射
    os.makedirs(os.path.join(BASE_DIR, "model"), exist_ok=True)
    recognizer.save(MODEL_SAVE_PATH)
    np.save(LABEL_MAP_PATH, label_map)

    print(f"模型保存至:{MODEL_SAVE_PATH}")
    print(f"标签映射:{label_map}")
    return recognizer, label_map


# 测试用
if __name__ == "__main__":
    train_model()
    print("模型训练完成!")

3.modules/realtime_recognize.py(实时识别)

import cv2
import numpy as np
import os

# 核心修改:绝对路径配置
BASE_DIR = os.path.dirname(os.path.dirname(os.path.abspath(__file__)))
MODEL_PATH = os.path.join(BASE_DIR, "model", "face_recognizer.yml")
LABEL_MAP_PATH = os.path.join(BASE_DIR, "model", "label_map.npy")


def realtime_recognize():
    """实时人脸识别"""
    # 加载模型和标签映射
    if not os.path.exists(MODEL_PATH) or not os.path.exists(LABEL_MAP_PATH):
        print("错误:未找到训练好的模型!请先运行模型训练步骤。")
        return

    recognizer = cv2.face.LBPHFaceRecognizer_create()
    recognizer.read(MODEL_PATH)
    label_map = np.load(LABEL_MAP_PATH, allow_pickle=True).item()
    reverse_label_map = {v: k for k, v in label_map.items()}  # 数字→人名

    # 加载人脸检测器
    face_cascade = cv2.CascadeClassifier(
        cv2.data.haarcascades + "haarcascade_frontalface_default.xml"
    )

    # 打开摄像头
    cap = cv2.VideoCapture(0)
    cap.set(cv2.CAP_PROP_FRAME_WIDTH, 640)
    cap.set(cv2.CAP_PROP_FRAME_HEIGHT, 480)

    print("实时识别中,按'q'退出...")
    while True:
        ret, frame = cap.read()
        if not ret:
            print("摄像头读取失败!")
            break

        gray = cv2.cvtColor(frame, cv2.COLOR_BGR2GRAY)
        faces = face_cascade.detectMultiScale(
            gray, scaleFactor=1.1, minNeighbors=5, minSize=(50, 50)
        )

        # 识别每一个检测到的人脸
        for (x, y, w, h) in faces:
            face_roi = gray[y:y + h, x:x + w]
            face_roi = cv2.resize(face_roi, (100, 100))  # 与训练数据尺寸一致

            # 预测(标签,置信度)
            label, confidence = recognizer.predict(face_roi)

            # 判断识别结果
            if confidence < 120:  # 先改成120,让它认出你
                person_name = reverse_label_map[label]
                text = f"{person_name} (置信度:{confidence:.1f})"
                color = (0, 255, 0)  # 绿色框
            else:
                text = "?????? (%.1f)" % confidence
                color = (0, 0, 255)

            # 绘制框和文字
            cv2.rectangle(frame, (x, y), (x + w, y + h), color, 2)
            cv2.rectangle(frame, (x, y - 30), (x + len(text) * 10, y), color, -1)
            cv2.putText(
                frame, text, (x, y - 10),
                cv2.FONT_HERSHEY_SIMPLEX, 0.6, (255, 255, 255), 2
            )

        cv2.imshow("Real-Time Face Recognition", frame)
        if cv2.waitKey(1) & 0xFF == ord("q"):
            break

    # 释放资源
    cap.release()
    cv2.destroyAllWindows()


# 测试用
if __name__ == "__main__":
    realtime_recognize()

2.2 创建主入口文件(main.py)

import sys
import os

# 将modules目录加入Python路径(确保能导入模块)
BASE_DIR = os.path.dirname(os.path.abspath(__file__))
sys.path.append(os.path.join(BASE_DIR, "modules"))

from data_preprocess import batch_extract
from model_train import train_model
from realtime_recognize import realtime_recognize

def main():
    print("===== 人脸识别项目 =====")
    print("1. 提取人脸ROI")
    print("2. 训练识别模型")
    print("3. 实时人脸识别")
    print("4. 运行全流程(1→2→3)")
    choice = input("请输入选择(1/2/3/4):")

    if choice == "1":
        print("\n开始提取人脸ROI...")
        batch_extract()
        print("ROI提取完成!")
    elif choice == "2":
        print("\n开始训练模型...")
        train_model()
        print("模型训练完成!")
    elif choice == "3":
        print("\n启动实时识别...")
        realtime_recognize()
    elif choice == "4":
        print("\n=== 运行全流程 ===")
        print("步骤1:提取ROI...")
        batch_extract()
        print("步骤2:训练模型...")
        train_model()
        print("步骤3:启动实时识别...")
        realtime_recognize()
    else:
        print("输入错误!请输入1-4。")

if __name__ == "__main__":
    main()

提示

需要子在data/raw_faces下创建以人名命名的文件夹(如zhangsan),放入 5-10 张该人的正面人脸图片;

如下图:

ScreenShot_2026-03-22_140704_657

代码运行结果如下:

pycharm64_kEIA0HXfE8

好了项目介绍就到这里了。

posted @ 2026-03-22 14:21  追风少年X  阅读(41)  评论(0)    收藏  举报