This week is the final week of my semester-long learning of Raspberry Pi, and I have finished my face recognition task on it. Here is my complete code👇
mian.py
mport Track_Display
import MyFaceRecognation
try:
print("Track_Display开启")
Track_Display.Track_Display()
print("Track_Display结束")
MyFaceRecognation.main()
except KeyboardInterrupt:
exit()
MyFaceRecognation.py
from sklearn.neighbors import NearestNeighbors
import face_recognition
import os
import json
import numpy as np
def save_to_json(encodings, names, json_file):
"""保存人脸特征和名字到 JSON 文件"""
# 将 numpy.ndarray 转换为列表
data = [{"name": name, "encoding": encoding.tolist()} for name, encoding in zip(names, encodings)]
with open(json_file, "w", encoding="utf-8") as f:
json.dump(data, f, ensure_ascii=False, indent=4)
def load_from_json(json_file):
"""从 JSON 文件加载人脸特征和名字"""
if not os.path.exists(json_file):
return [], []
with open(json_file, "r", encoding="utf-8") as f:
data = json.load(f)
encodings = [np.array(item["encoding"]) for item in data] # 转回 numpy.ndarray
names = [item["name"] for item in data]
return encodings, names
def load_known_faces_with_knn(known_faces_dir, data_file="data/faces.json"):
"""加载已知人脸,构建 KD-Tree,支持增量更新和同步删除"""
print("Loading known faces...")
os.makedirs(os.path.dirname(data_file), exist_ok=True)
# 加载已保存的特征和名称
known_encodings, known_names = load_from_json(data_file)
# 获取目录中的实际文件名(去掉扩展名)
current_files = {os.path.splitext(filename)[0] for filename in os.listdir(known_faces_dir) if filename.endswith((".jpg", ".jpeg", ".png"))}
# 找到需要移除的文件
removed_files = set(known_names) - current_files
if removed_files:
print(f"Removing deleted files from cache: {removed_files}")
indices_to_keep = [i for i, name in enumerate(known_names) if name not in removed_files]
known_encodings = [known_encodings[i] for i in indices_to_keep]
known_names = [known_names[i] for i in indices_to_keep]
# 处理新文件
for filename in current_files - set(known_names):
print(f"Processing {filename}...")
image_path = os.path.join(known_faces_dir, filename + ".jpg") # 默认扩展名为 .jpg
if not os.path.exists(image_path): # 检查其他扩展名
image_path = os.path.join(known_faces_dir, filename + ".jpeg")
if not os.path.exists(image_path): # 检查其他扩展名
image_path = os.path.join(known_faces_dir, filename + ".png")
if os.path.exists(image_path):
image = face_recognition.load_image_file(image_path)
encodings = face_recognition.face_encodings(image)
if encodings: # 确保图片中有检测到人脸
known_encodings.append(encodings[0])
known_names.append(filename)
# 保存更新后的特征和名称
save_to_json(known_encodings, known_names, data_file)
knn = NearestNeighbors(n_neighbors=1, algorithm="ball_tree").fit(known_encodings)
return knn, np.array(known_encodings), known_names
def recognize_faces_with_knn(input_image_path, knn, known_names, tolerance=0.6):
"""使用 KD-Tree 识别人脸"""
print("Recognizing faces in image...")
input_image = face_recognition.load_image_file(input_image_path)
face_encodings = face_recognition.face_encodings(input_image)
recognized_names = []
for face_encoding in face_encodings:
distances, indices = knn.kneighbors([face_encoding])
if distances[0][0] < tolerance:
recognized_names.append(known_names[indices[0][0]])
else:
recognized_names.append("Unknown")
return recognized_names
def main():
print("Starting...")
known_faces_dir = "./data/image" # 存放已知人脸图片的目录
input_image_path = "./data/output.jpeg" # 要识别的图片路径
knn, known_encodings, known_names = load_known_faces_with_knn(known_faces_dir)
print("Known names (without extensions):", known_names)
recognized_names = recognize_faces_with_knn(input_image_path, knn, known_names)
print("**********************")
print("Recognized names:", recognized_names)
print("**********************")
Track_Display.py
import cv2
from picamera2 import Picamera2
import libcamera
from adafruit_servokit import ServoKit
import time
step = 1
# 初始化伺服电机控制
kit = ServoKit(channels=16)
temp = 0
temp1 = 0
temp2 = 0
temp3 = 0
temp4 = 0
# 初始伺服角度
pan = 90
tilt = 90
kit.servo[0].angle = pan
kit.servo[1].angle = tilt
# 摄像头分辨率
dispW, dispH = 640, 480
# 人脸检测模型加载
face_cascade = cv2.CascadeClassifier('./source/haarcascade_frontalface_default.xml')
def Track_Display():
"""摄像头视频采集与人脸检测"""
global pan, tilt,temp1,temp2,temp3,temp4,temp
# 初始化摄像头
picamera = Picamera2()
config = picamera.create_preview_configuration(
main={"format": 'RGB888', "size": (dispW, dispH)},
raw={"format": "SRGGB12", "size": (1920, 1080)}
)
config["transform"] = libcamera.Transform(hflip=1, vflip=1)
picamera.configure(config)
picamera.start()
try:
while True:
# 捕获帧
time.sleep(0.3)
frame = picamera.capture_array()
frame[..., [0, 2]] = frame[..., [2, 0]] # B 和 R 通道交换
# 转换为灰度图
gray = cv2.cvtColor(frame, cv2.COLOR_RGB2GRAY)
faces = face_cascade.detectMultiScale(gray, 1.3, 15)
# 人脸检测与伺服控制
for (x, y, w, h) in faces:
cv2.rectangle(frame, (x, y), (x + w, y + h), (0, 0, 255), 2)
Xcent = x + w / 2
Ycent = y + h / 2
# 限制伺服角度范围
pan = max(0, min(180, pan))
tilt = max(0, min(180, tilt))
if (Xcent - dispW / 2) >=35:
pan -= step
else:
temp1 = 1
if (Xcent - dispW / 2) <=-35:
pan += step
else:
temp2 = 1
if (Ycent - dispH / 2) >= 35:
tilt += step
else:
temp3 = 1
if (Ycent - dispH / 2) <= -35:
tilt -= step
else:
temp4 = 1
if temp1 ==1 and temp2 == 1 and temp3 == 1 and temp4 == 1:
temp = 1
if temp == 1:
frame = cv2.cvtColor(frame, cv2.COLOR_RGB2BGR)
print ("拍摄中~")
cv2.imwrite("./data/output.jpeg",frame)
print ("图片拍摄完成!")
break
kit.servo[0].angle = pan
kit.servo[1].angle = tilt
#//time.sleep(0.1)
if temp == 1:
break
else:
temp1 = 0
temp2 = 0
temp3 = 0
temp4 = 0
# 显示图像
frame = cv2.cvtColor(frame, cv2.COLOR_RGB2BGR)
cv2.imshow("Camera Output", frame)
# 按 'q' 退出
if cv2.waitKey(1) & 0xFF == ord('q'):
break
finally:
# 释放资源
picamera.stop()
cv2.destroyAllWindows()
Compared to the system last week, the best improvement of my code is that there is no need for users to enter their names manually because the system can recognize who you are automatically, once I put a photo into the database, the system can extract features automatically, and write the features into a .json file, and when the camera take a photo, it can compare the face with .json file and get the answer immediately, here is the video👇