-
Notifications
You must be signed in to change notification settings - Fork 0
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
- Loading branch information
Showing
3 changed files
with
396 additions
and
0 deletions.
There are no files selected for viewing
Binary file added
BIN
+238 KB
raspberrypi/use-camera-face-recognition-with-opencv-picamera2/dphys-swapfile.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
396 changes: 396 additions & 0 deletions
396
raspberrypi/use-camera-face-recognition-with-opencv-picamera2/index.md
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,396 @@ | ||
--- | ||
title: "使用Face Recognition进行人脸识别" | ||
date: "2024-06-12" | ||
categories: | ||
- "树莓派" | ||
tags: | ||
- "RaspberryPi" | ||
order: 6 | ||
--- | ||
## 替换apt和pip3镜像源(可选) | ||
在开始安装各种依赖库之前,最好先更换镜像源,执行如下命令: | ||
```bash | ||
sudo cp /etc/apt/sources.list /etc/apt/sources.list.bak | ||
sudo vi /etc/apt/sources.list | ||
# 替换sources.list文件内容 | ||
# 默认注释了源码镜像以提高 apt update 速度,如有需要可自行取消注释 | ||
deb http://mirrors.tuna.tsinghua.edu.cn/debian/ bookworm main contrib non-free non-free-firmware | ||
# deb-src http://mirrors.tuna.tsinghua.edu.cn/debian/ bookworm main contrib non-free non-free-firmware | ||
|
||
deb http://mirrors.tuna.tsinghua.edu.cn/debian/ bookworm-updates main contrib non-free non-free-firmware | ||
# deb-src http://mirrors.tuna.tsinghua.edu.cn/debian/ bookworm-updates main contrib non-free non-free-firmware | ||
|
||
deb http://mirrors.tuna.tsinghua.edu.cn/debian/ bookworm-backports main contrib non-free non-free-firmware | ||
# deb-src http://mirrors.tuna.tsinghua.edu.cn/debian/ bookworm-backports main contrib non-free non-free-firmware | ||
|
||
# 以下安全更新软件源包含了官方源与镜像站配置,如有需要可自行修改注释切换 | ||
deb https://security.debian.org/debian-security bookworm-security main contrib non-free non-free-firmware | ||
# deb-src https://security.debian.org/debian-security bookworm-security main contrib non-free non-free-firmware | ||
|
||
sudo vi /etc/apt/sources.list.d/raspi.list | ||
#替换 | ||
deb https://mirrors.tuna.tsinghua.edu.cn/raspberrypi/ bookworm main | ||
|
||
|
||
sudo apt update && sudo apt -y upgrade | ||
|
||
sudo vi /etc/pip.conf | ||
[global] | ||
index-url = https://pypi.tuna.tsinghua.edu.cn/simple | ||
# extra-index-url = https://www.piwheels.org/simple | ||
``` | ||
## 安装face_recognition库依赖 | ||
在进行人脸识别的时候需要用到`face_recognition`、`opencv`和`imtuils`等库,这些库在Raspberry Pi OS上安装比较麻烦,需要创建一个Python的虚拟环境,并切换到虚拟环境中去安装,首先创建一个虚拟环境: | ||
```bash | ||
# 使用 venv 模块 | ||
python3 -m venv myenv --system-site-packages | ||
``` | ||
`--system-site-packages`这个参数为了能使用系统级别的库,不然可能很多基础库要重新安装,之后切换使用虚拟环境: | ||
```bash | ||
source myenv/bin/activate | ||
``` | ||
这时候就能在你的命令行前面看到虚拟环境的前缀,之后执行如下安装命令: | ||
```bash | ||
|
||
# 安装相关依赖包 | ||
sudo apt -y update && sudo apt -y full-upgrade | ||
sudo apt install cmake build-essential pkg-config git | ||
|
||
sudo apt install libjpeg-dev libjxl-dev libtiff-dev libpng-dev libwebp-dev libopenexr-dev | ||
sudo apt install libavdevice-dev libavfilter-dev libavformat-dev libopenblas-dev | ||
|
||
sudo apt install libavcodec-dev libavformat-dev libswresample-dev libswscale-dev libv4l-dev libxvidcore-dev libx264-dev libdc1394-dev libgstreamer-plugins-base1.0-dev libgstreamer1.0-dev libavutil-dev | ||
|
||
sudo apt install libgtk-3-dev python3-pyqt5 | ||
sudo apt install libqt5gui5 libqt5webkit5 libqt5test5 | ||
sudo apt install libatlas-base-dev liblapacke-dev gfortran | ||
|
||
sudo apt install libhdf5-dev libhdf5-103 | ||
sudo apt install python3-dev python3-pip python3-numpy | ||
sudo apt install fonts-wqy-zenhei | ||
|
||
pip3 install imutils | ||
pip3 install opencv-contrib-python | ||
``` | ||
在安装`face_recognition`之前最好先把swap改大点,不然容易奔溃: | ||
```bash | ||
sudo vi /etc/dphys-swapfile | ||
``` | ||
把`CONF_SWAPSIZE = 100` 改成 `CONF_SWAPSIZE=2048`,等下安装完成后再改回来 | ||
|
||
![dphys-swapfile ](dphys-swapfile.png) | ||
|
||
改好后运行如下命令进行生效: | ||
```bash | ||
sudo systemctl restart dphys-swapfile | ||
``` | ||
```bash | ||
pip3 install face_recognition -vvv #这个安装很慢,-vvv可以查看具体进度 | ||
``` | ||
这条命令需要运行一段时间,耐心等待,大概一个半小时左右。 | ||
![pip3-install-face_recognition](pip3-install-face_recognition.png) | ||
::: info | ||
如果有更快速安装face_recognition库的办法,请告诉我,不胜感激。 | ||
::: | ||
## 获取头像数据 | ||
前面已经实现了摄像头的人脸检测,下面使用`face_recognition`库实现下人脸识别,人脸检测和识别是两个不同的概念,检测是知道图片上有人脸,识别是知道这张脸是谁,要让计算机知道你是谁,首先你要告诉他你是谁,我们就要给他准备我们自己的人脸照片,让他先训练识别这个人是我,首先是如下代码: | ||
```python | ||
from picamera2 import Picamera2, Preview | ||
import cv2 | ||
import numpy as np | ||
import os | ||
|
||
# 提示用户输入名字 | ||
name = input('请输入你的名字: ') | ||
|
||
# 如果目录不存在,则创建目录 | ||
os.makedirs(f"dataset/{name}", exist_ok=True) | ||
|
||
# 初始化摄像头 | ||
picam2 = Picamera2() | ||
config = picam2.create_preview_configuration(main={"size": (512, 304)}) | ||
picam2.configure(config) | ||
picam2.start() | ||
|
||
img_counter = 0 | ||
|
||
while True: | ||
# 捕获一帧图像 | ||
frame = picam2.capture_array() | ||
|
||
# 将图像从 RGB 转换为 BGR | ||
frame_bgr = cv2.cvtColor(frame, cv2.COLOR_RGB2BGR) | ||
|
||
# 显示图像 | ||
cv2.imshow("Press Space to take a photo", frame_bgr) | ||
|
||
# 处理按键事件 | ||
k = cv2.waitKey(1) | ||
if k % 256 == 27: # 按下 ESC 键 | ||
print("按下 ESC 键,关闭...") | ||
break | ||
elif k % 256 == 32: # 按下空格键 | ||
img_name = f"dataset/{name}/image_{img_counter}.jpg" | ||
# 保存图像 | ||
cv2.imwrite(img_name, frame_bgr) | ||
print(f"{img_name} 已保存!") | ||
img_counter += 1 | ||
|
||
cv2.destroyAllWindows() | ||
picam2.stop() | ||
|
||
``` | ||
运行上面代码,会弹出一个摄像头预览窗口,按空格键进行截图保存你的面部数据,至少在10张以上的数据比较好,头转动要慢点,不然容易模糊。 | ||
|
||
## 训练模型 | ||
有了人脸的数据,接下去就需要让计算机处理这些数据,运行如下代码: | ||
```python | ||
# 导入必要的包 | ||
from imutils import paths | ||
import face_recognition | ||
import pickle | ||
import cv2 | ||
import os | ||
|
||
# 图片位于 dataset 文件夹中 | ||
print("[INFO] 开始处理人脸...") | ||
imagePaths = list(paths.list_images("dataset")) | ||
|
||
# 初始化已知的编码和名字列表 | ||
knownEncodings = [] | ||
knownNames = [] | ||
|
||
# 遍历图片路径 | ||
for (i, imagePath) in enumerate(imagePaths): | ||
# 从图片路径中提取人物名字 | ||
print("[INFO] 处理图像 {}/{}".format(i + 1, len(imagePaths))) | ||
name = imagePath.split(os.path.sep)[-2] | ||
print(f"[DEBUG] 当前处理的图片路径: {imagePath}") | ||
print(f"[DEBUG] 当前处理的名字: {name}") | ||
|
||
# 读取输入图像并将其从 BGR (OpenCV 读取顺序) 转换为 RGB (dlib 使用的顺序) | ||
image = cv2.imread(imagePath) | ||
rgb = cv2.cvtColor(image, cv2.COLOR_BGR2RGB) | ||
print(f"[DEBUG] 图像读取并转换为 RGB 格式") | ||
|
||
# 检测输入图像中每张人脸的 (x, y)-坐标的边界框 | ||
boxes = face_recognition.face_locations(rgb, model="hog") | ||
print(f"[DEBUG] 检测到 {len(boxes)} 张人脸") | ||
|
||
# 计算人脸的嵌入(即人脸编码) | ||
encodings = face_recognition.face_encodings(rgb, boxes) | ||
print(f"[DEBUG] 计算到 {len(encodings)} 个编码") | ||
|
||
# 遍历每一个编码 | ||
for encoding in encodings: | ||
# 将每个编码和名字添加到已知的编码和名字列表中 | ||
knownEncodings.append(encoding) | ||
knownNames.append(name) | ||
print(f"[DEBUG] 已知编码和名字列表更新") | ||
|
||
# 将人脸编码和名字序列化到磁盘中 | ||
print("[INFO] 序列化编码...") | ||
data = {"encodings": knownEncodings, "names": knownNames} | ||
f = open("encodings.pickle", "wb") | ||
f.write(pickle.dumps(data)) | ||
f.close() | ||
print("[INFO] 编码序列化完成") | ||
|
||
``` | ||
处理好这些数据之后,就可以给后续识别使用。 | ||
## 人脸识别 | ||
人脸识别的主要代码如下: | ||
```python | ||
from picamera2 import Picamera2, Preview | ||
import cv2 | ||
import face_recognition | ||
import pickle | ||
import time | ||
from PIL import Image, ImageDraw, ImageFont | ||
import numpy as np | ||
|
||
# 初始化当前识别的名字为“未知” | ||
currentname = "Unknown" | ||
# 加载 encodings.pickle 文件 | ||
encodingsP = "encodings.pickle" | ||
|
||
# 加载已知的面部和嵌入 | ||
print("[INFO] 正在加载 encodings + 人脸检测器...") | ||
try: | ||
with open(encodingsP, "rb") as f: | ||
data = pickle.load(f) | ||
print("[INFO] encodings 加载成功") | ||
except Exception as e: | ||
print(f"[ERROR] 无法加载 encodings: {e}") | ||
exit() | ||
|
||
# 初始化摄像头 | ||
print("[INFO] 初始化摄像头...") | ||
try: | ||
picam2 = Picamera2() | ||
# 创建配置 | ||
config = picam2.create_preview_configuration(main={"size": (640, 480)}) | ||
picam2.configure(config) | ||
|
||
picam2.start() | ||
|
||
time.sleep(2.0) | ||
print("[INFO] 摄像头初始化成功") | ||
except Exception as e: | ||
print(f"[ERROR] 摄像头初始化失败: {e}") | ||
exit() | ||
|
||
# 创建窗口 | ||
cv2.namedWindow("Face Recognition Running", cv2.WINDOW_NORMAL) | ||
print("[INFO] 创建窗口之后") | ||
|
||
# 开始 FPS 计数器 | ||
fps_start_time = time.time() | ||
print("[INFO] 开始 FPS 计数器之后") | ||
frame_count = 0 | ||
|
||
# 加载中文字体 | ||
font_path = "/usr/share/fonts/truetype/wqy/wqy-zenhei.ttc" # 确保字体文件存在 | ||
try: | ||
font = ImageFont.truetype(font_path, 30) # 字体大小调整为 30 | ||
except Exception as e: | ||
print(f"[ERROR] 无法加载字体: {e}") | ||
exit() | ||
|
||
while True: | ||
try: | ||
frame_start_time = time.time() | ||
|
||
# 捕获一帧图像 | ||
frame = picam2.capture_array() | ||
capture_time = time.time() | ||
print(f"[TIME] 捕获图像时间: {capture_time - frame_start_time:.4f} 秒") | ||
|
||
# 将图像从 RGB 转换为 BGR | ||
frame_bgr = cv2.cvtColor(frame, cv2.COLOR_RGB2BGR) | ||
conversion_time = time.time() | ||
print(f"[TIME] 图像转换时间: {conversion_time - capture_time:.4f} 秒") | ||
|
||
frame_gray = cv2.cvtColor(frame_bgr, cv2.COLOR_BGR2GRAY) | ||
gray_conversion_time = time.time() | ||
print(f"[TIME] 灰度转换时间: {gray_conversion_time - conversion_time:.4f} 秒") | ||
|
||
# 检测人脸框 | ||
boxes = face_recognition.face_locations(frame_gray) | ||
detection_time = time.time() | ||
print(f"[TIME] 人脸检测时间: {detection_time - gray_conversion_time:.4f} 秒") | ||
print(f"[INFO] 检测到 {len(boxes)} 张人脸") | ||
|
||
# 初始化时间变量 | ||
encoding_time = detection_time | ||
draw_time = detection_time | ||
|
||
# 为每个人脸边界框计算人脸嵌入 | ||
if len(boxes) > 0: | ||
try: | ||
encodings = face_recognition.face_encodings(frame_bgr, boxes) | ||
encoding_time = time.time() | ||
print(f"[TIME] 计算人脸嵌入时间: {encoding_time - detection_time:.4f} 秒") | ||
print(f"[INFO] 计算了 {len(encodings)} 个人脸嵌入") | ||
|
||
if len(encodings) == 0: | ||
print("[WARNING] 未计算到人脸嵌入,跳过当前帧") | ||
continue | ||
except Exception as e: | ||
print(f"[ERROR] 计算人脸嵌入时发生错误: {e}") | ||
continue # 跳过当前帧继续下一帧 | ||
|
||
names = [] | ||
|
||
# 遍历人脸嵌入 | ||
for encoding in encodings: | ||
# 尝试将输入图像中的每个人脸与我们已知的嵌入进行匹配 | ||
matches = face_recognition.compare_faces(data["encodings"], encoding) | ||
print(f"[INFO] 找到匹配: {matches}") | ||
name = "未知" # 如果未识别人脸,则打印“未知” | ||
|
||
# 检查是否找到匹配项 | ||
if True in matches: | ||
# 找到所有匹配人脸的索引,然后初始化一个字典来计算每个人脸被匹配的总次数 | ||
matchedIdxs = [i for (i, b) in enumerate(matches) if b] | ||
counts = {} | ||
|
||
# 遍历匹配的索引并维护每个被识别人脸的计数 | ||
for i in matchedIdxs: | ||
name = data["names"][i] | ||
counts[name] = counts.get(name, 0) + 1 | ||
|
||
# 确定得到票数最多的已识别人脸(注意:在不太可能的平局情况下,Python 将选择字典中的第一个条目) | ||
name = max(counts, key=counts.get) | ||
|
||
# 如果识别到数据集中的人,则在屏幕上打印他们的姓名 | ||
if currentname != name: | ||
currentname = name | ||
print(f"[INFO] 识别到: {currentname}") | ||
|
||
# 更新姓名列表 | ||
names.append(name) | ||
|
||
draw_start_time = time.time() | ||
# 将 OpenCV 图像转换为 PIL 图像 | ||
pil_image = Image.fromarray(cv2.cvtColor(frame_bgr, cv2.COLOR_BGR2RGB)) | ||
draw = ImageDraw.Draw(pil_image) | ||
|
||
# 遍历已识别人脸 | ||
for ((top, right, bottom, left), name) in zip(boxes, names): | ||
try: | ||
print(f"[INFO] 绘制人脸框和姓名: {name}, 框: {top, right, bottom, left}") | ||
# 在图像上绘制预测的人脸姓名 - 颜色为绿色 | ||
draw.rectangle([(left, top), (right, bottom)], outline=(255, 0, 0), width=2) # 红色边框 | ||
y = top - 30 if top - 30 > 30 else top + 30 | ||
draw.text((left, y), name, font=font, fill=(0, 255, 0)) # 绿色字体 | ||
except Exception as e: | ||
print(f"[ERROR] 绘制人脸框和姓名时出错: {e}") | ||
draw_time = time.time() | ||
print(f"[TIME] 绘制时间: {draw_time - draw_start_time:.4f} 秒") | ||
|
||
# 将 PIL 图像转换回 OpenCV 图像 | ||
frame_bgr = cv2.cvtColor(np.array(pil_image), cv2.COLOR_RGB2BGR) | ||
|
||
# 在屏幕上显示图像 | ||
cv2.imshow("Face Recognition Running", frame_bgr) | ||
key = cv2.waitKey(1) & 0xFF | ||
waitkey_time = time.time() | ||
print(f"[TIME] waitKey 时间: {waitkey_time - draw_time:.4f} 秒") | ||
print("[INFO] 调用 waitKey 完成") | ||
|
||
# 按 'q' 键退出 | ||
if key == ord("q"): | ||
break | ||
|
||
# 更新 FPS 计数器 | ||
frame_count += 1 | ||
if frame_count >= 10: | ||
elapsed_time = time.time() - fps_start_time | ||
fps = frame_count / elapsed_time | ||
print(f"[INFO] 每秒帧数 (FPS): {fps:.2f}") | ||
frame_count = 0 | ||
fps_start_time = time.time() | ||
|
||
frame_end_time = time.time() | ||
print(f"[TIME] 处理一帧总时间: {frame_end_time - frame_start_time:.4f} 秒") | ||
|
||
except Exception as e: | ||
print(f"[ERROR] 在主循环中发生错误: {e}") | ||
break | ||
|
||
# 清理工作 | ||
cv2.destroyAllWindows() | ||
picam2.stop() | ||
print("[INFO] 清理完成,程序退出") | ||
|
||
|
||
``` | ||
|
||
## 参考资料 | ||
[https://github.com/carolinedunn/facial_recognition](https://github.com/carolinedunn/facial_recognition) | ||
[https://github.com/justsaumit/opencv-face-recognition-rpi4](https://github.com/justsaumit/opencv-face-recognition-rpi4) | ||
[https://github.com/ageitgey/face_recognition/blob/master/examples/facerec_from_webcam_faster.py](https://github.com/ageitgey/face_recognition/blob/master/examples/facerec_from_webcam_faster.py) | ||
|
||
[https://pypi.org/project/face-recognition/#files](https://pypi.org/project/face-recognition/#files) | ||
[https://mirrors.tuna.tsinghua.edu.cn/help/debian/](https://mirrors.tuna.tsinghua.edu.cn/help/debian/) |
Binary file added
BIN
+83.5 KB
...camera-face-recognition-with-opencv-picamera2/pip3-install-face_recognition.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.