树莓派的OpenCV的人脸识别开锁

文档结尾有代码下载链接

🎯 实现效果

  • 场景 1:“主人” 面对摄像头→ 屏幕显示 “已识别:主人”,绿色框框选人脸,继电器通电,门锁打开;
    在这里插入图片描述

在这里插入图片描述
在这里插入图片描述

  • 场景 2:陌生人面对摄像头→ 屏幕显示 “陌生人”,红色框框选人脸,继电器保持关闭,门锁不动;
    在这里插入图片描述

在这里插入图片描述

在这里插入图片描述

  • 场景 3:无一人→ 屏幕显示 “未检测到人脸”,继电器关闭。

在这里插入图片描述
在这里插入图片描述

  • 退出程序:按键盘 “q” 键,程序自动释放摄像头、关闭继电器,安全退出。

在这里插入图片描述

🌟 系统组成

  • 树莓派主板:树莓派4或树莓派5都可以;
  • 摄像头模块:选择USB摄像头;
  • 显示屏:小尺寸HDMI显示屏可实时显示识别信息;
  • 网络模块:树莓派4和5自带WiFi/以太网,确保设备能连接互联网,用于数据上传;
  • 电源:5V/3Amicro-USB电源,保证树莓派稳定运行,避免因供电不足导致识别中断。
  • 继电器模块:5V 单路继电器控制门锁(电磁锁 / 舵机门锁)。

🌟 驱动思路

1.初始化配置

  • 继电器配置:选用LGPIO驱动 GPIO,固定RELAY_BCM_PIN=17,设置初始值为False,用OutputDevice封装继电器操作,简化 “开 / 关 / 释放” 逻辑。
  • 人脸识别配置:TARGET_FACE_ID=1:与前期 “样本采集程序” 的目标 ID 严格一致;CONFIDENCE_THRESHOLD=90:LBPH 模型的置信度阈值(注:LBPH 置信度越小表示匹配度越高,90 是平衡 “严格性与容错性” 的常用值 —— 既避免陌生人误触发,也允许目标人脸因光照、角度轻微变化导致的置信度波动);MODEL_PATH:指向前期训练好的模型文件(若模型缺失则直接退出,避免无意义运行)。
  • 中文显示配置:通过PIL将 OpenCV 帧转为 RGB 格式,用ImageDraw绘制中文,再转回 BGR 格式;

2.摄像头捕获

(1). 多线程分工

  • 后台线程_reader:持续调用摄像头cap.read()采集帧,若队列满则丢弃旧帧(只保留最新 3 帧,maxsize=3),避免帧堆积导致延迟;
  • 主线程read():从队列中获取最新帧,无需等待摄像头采集,确保后续人脸检测、识别流程流畅。

(2). 资源控制

  • 设线程为daemon=True:主程序退出时后台线程自动终止,避免资源泄漏;
  • 提供terminate()方法:统一释放摄像头资源,配合后续 “程序退出时的资源清理”。

🌟 5.树莓派上部署运行

a. 搭建环境(树莓派端)

  • 先安装所有依赖库,打开终端输命令安装。

b. 采集人脸样本,训练识别模型

  • 创建样本文件夹:在树莓派上新建faceImage文件夹,放入 20 张以上 “主人” 的人脸照片(建议不同角度、光照,保证样本多样性)。
  • 运行样本训练脚本:执行之前的LBPH.py,脚本会自动从照片中提取人脸、标准化处理,最终生成target_face_model.yml模型文件(若样本不足 20 个,建议补充后再训练,提高识别准确率)。
  • 验证模型:脚本运行成功后,项目文件夹中会出现模型文件,说明 “主人” 的人脸特征已被记录。

c. 核心代码部署

  • 注意修改 3 个关键参数:
  • RELAY_BCM_PIN:继电器连接的树莓派 BCM 引脚(如 17,需与硬件接线一致);
  • TARGET_FACE_ID:与样本训练时的 ID 保持一致(默认 1);
  • MODEL_PATH:指向训练好的target_face_model.yml路径。

d. 硬件接线

  • 继电器 “VCC”→ 树莓派 5V 引脚;
  • 继电器 “GND”→ 树莓派 GND 引脚;
  • 继电器 “IN”→ 树莓派 BCM 引脚
  • 摄像头连接:USB 摄像头直接插树莓派 USB 口

E. 启动程序

  • 在终端执行命令,启动人脸识别开锁系统:若是使用虚拟python,则需先进入虚拟环境再运行。
    python3 OpenCVFace.py
    在这里插入图片描述

🎯 程序代码

OpenCVFace.py

# -*- coding: utf-8 -*-
import cv2
import queue  
import threading  
import time  
from gpiozero import OutputDevice
from gpiozero.pins.lgpio import LGPIOFactory  
from PIL import Image, ImageDraw, ImageFont  
import numpy as np  

#虚拟环境下安装 pip install lgpio
# -------------- 初始化配置 --------------
# 继电器配置
RELAY_BCM_PIN = 17
lgpio_factory = LGPIOFactory()
relay = OutputDevice(
    RELAY_BCM_PIN, 
    active_high=True, 
    initial_value=False,
    pin_factory=lgpio_factory
)

# 人脸识别核心配置
TARGET_FACE_ID = 1  # 与样本采集时的ID一致
CONFIDENCE_THRESHOLD = 90  # 置信度阈值(越小越严格,建议70-90)
MODEL_PATH = "./target_face_model.yml"  # 训练好的模型路径

# 中文显示函数(复用原有逻辑)
def draw_chinese_text(img, text, position, color=(0, 255, 0), size=20):
    img_pil = Image.fromarray(cv2.cvtColor(img, cv2.COLOR_BGR2RGB))
    draw = ImageDraw.Draw(img_pil)
    font_paths = [
        "/usr/share/fonts/truetype/wqy/wqy-microhei.ttc",
        "/usr/share/fonts/truetype/simhei/simhei.ttf",
        ImageFont.load_default()
    ]
    font = None
    for path in font_paths:
        try:
            font = ImageFont.truetype(path, size, encoding="utf-8")
            break
        except:
            continue
    draw.text(position, text, color, font=font)
    return cv2.cvtColor(np.array(img_pil), cv2.COLOR_RGB2BGR)


# -------------- 摄像头捕获类(复用原有逻辑) --------------
class VideoCapture:
    def __init__(self, camera_id):
        self.cap = cv2.VideoCapture(camera_id)
        self.cap.set(cv2.CAP_PROP_FRAME_WIDTH, 640)
        self.cap.set(cv2.CAP_PROP_FRAME_HEIGHT, 480)
        self.q = queue.Queue(maxsize=3)
        self.stop_threads = False    
        th = threading.Thread(target=self._reader)
        th.daemon = True             
        th.start()

    def _reader(self):
        while not self.stop_threads:
            ret, frame = self.cap.read()
            if not ret:
                break
            if not self.q.empty():
                try:
                    self.q.get_nowait() 
                except queue.Empty:
                    pass
            self.q.put(frame)

    def read(self):
        return self.q.get()
    
    def terminate(self):
        self.stop_threads = True
        self.cap.release()


# -------------- 主程序 --------------
if __name__ == "__main__":
    print(f"OpenCV版本: {cv2.__version__}")
    
    # 1. 初始化摄像头
    cap = VideoCapture(0)
    if not cap.cap.isOpened():
        print("❌ 无法打开摄像头")
        cap.terminate()
        relay.off()
        relay.close()
        exit()
    
    # 2. 加载人脸检测器和识别模型
    # 人脸检测器(检测是否有人脸)
    face_cascade = cv2.CascadeClassifier('./haarcascade_frontalface_default.xml')
    if face_cascade.empty():
        print("❌ 无法加载人脸分类器")
        cap.terminate()
        relay.off()
        relay.close()
        exit()
    # 人脸识别模型(判断是否为指定人脸)
    recognizer = cv2.face.LBPHFaceRecognizer_create()
    try:
        recognizer.read(MODEL_PATH)
    except:
        print(f"❌ 无法加载模型文件,请先运行样本采集程序生成 {MODEL_PATH}")
        cap.terminate()
        relay.off()
        relay.close()
        exit()
    print("✅ 模型加载成功,开始指定人脸检测(按'q'退出)")
    
    # 3. 创建显示窗口
    cv2.namedWindow("人脸开锁系统", cv2.WINDOW_NORMAL)
    cv2.resizeWindow("人脸开锁系统", 680, 500)
    
    try:
        while True:
            frame = cap.read()
            if frame is None:
                print("⚠️ 未获取到摄像头帧")
                time.sleep(0.1)
                continue
            
            # 4. 检测人脸(先找画面中的人脸位置)
            gray = cv2.cvtColor(frame, cv2.COLOR_BGR2GRAY)
            faces = face_cascade.detectMultiScale(gray, 1.1, 5)
            
            # 5. 人脸识别与继电器控制
            relay_status = "关闭"  # 继电器状态
            face_status = "未检测到人脸"  # 人脸状态(显示用)
            
            if len(faces) > 0:
                # 对每个检测到的人脸进行识别(默认只处理单张人脸)
                x, y, w, h = faces[0]
                # 提取人脸区域(灰度图,与训练样本尺寸一致)
                face_roi = cv2.resize(gray[y:y+h, x:x+w], (100, 100))
                # 识别:返回(匹配的ID,置信度),置信度越小匹配度越高
                predicted_id, confidence = recognizer.predict(face_roi)
                
                # 判断是否为指定人脸
                if predicted_id == TARGET_FACE_ID and confidence < CONFIDENCE_THRESHOLD:
                    relay.on()
                    relay_status = "打开"
                    #face_status = f"已识别:主人(置信度:{confidence:.1f})"
                    face_status = f"已识别:主人"
                    box_color = (0, 255, 0)  # 匹配成功:绿色框
                else:
                    relay.off()
                    relay_status = "关闭"
                    #face_status = f"陌生人(置信度:{confidence:.1f})"
                    face_status = f"陌生人"
                    box_color = (0, 0, 255)  # 匹配失败:红色框
                
                # 绘制人脸框
                cv2.rectangle(frame, (x, y), (x+w, y+h), box_color, 2)
            
            # 6. 绘制状态文字
            frame = draw_chinese_text(frame, f"继电器状态:{relay_status}", (10, 30), (0, 255, 0), 25)
            frame = draw_chinese_text(frame, face_status, (10, 70), (0, 255, 255), 25)
            
            # 7. 显示画面
            cv2.imshow("人脸开锁系统", frame)
            
            # 按q退出
            if cv2.waitKey(1) & 0xFF == ord('q'):
                break
    finally:
        # 释放资源
        cap.terminate()
        cv2.destroyAllWindows()
        relay.off()
        relay.close()
        print("✅ 程序退出,资源已释放")

🎯 代码下载链接

https://download.csdn.net/download/qq_41954594/92211413

更多推荐