一天到晚闲的没事干,用Raspberry Pi + OpenCV + Streamlit 做了一个人脸识别
2023-12-14 15:23:22
介绍
如何创建面部识别应用程序的记录,具体来说,所做如下:
- 尝试使用Raspberry Pi 4 + CameraModule3 进行拍摄
- 尝试使用 OpenCV 对相机拍摄的图像进行面部识别处理
- 尝试使用 Streamlit 将以上内容转换为 Web 应用程序
开发环境
- Raspberry Pi 4
- Bullseye 64bit
- Python 3.9.2
- CameraModule3
使用 Raspberry Pi 和相机拍摄
在这个步骤中遇到了很多麻烦,过几天再出篇文章,单独进行总结
使用 OpenCV 对拍摄图像进行人脸识别处理
安装包
安装 picamera2 和 OpenCV。
pip install picamera2
pip install opencv-python
下载训练好的模型
yunet_n_640_640.onnx
face_recognizer_fast.onnx
保存面部图像
从图像中检测人脸,将其剪切出来并保存为人脸图像。
源代码
import os
import argparse
import cv2
def main():
parser = argparse.ArgumentParser("generate aligned face images from an image")
parser.add_argument("image", help="input image file path (./image.jpg)")
args = parser.parse_args()
path = args.image
directory = os.path.dirname(args.image)
if not directory:
directory = os.path.dirname(__file__)
path = os.path.join(directory, args.image)
image = cv2.imread(path)
if image is None:
exit()
channels = 1 if len(image.shape) == 2 else image.shape[2]
if channels == 1:
image = cv2.cvtColor(image, cv2.COLOR_GRAY2BGR)
if channels == 4:
image = cv2.cvtColor(image, cv2.COLOR_BGRA2BGR)
weights = "onnx/yunet_n_640_640.onnx"
face_detector = cv2.FaceDetectorYN_create(weights, "", (0, 0))
weights = "onnx/face_recognizer_fast.onnx"
face_recognizer = cv2.FaceRecognizerSF_create(weights, "")
height, width, _ = image.shape
face_detector.setInputSize((width, height))
_, faces = face_detector.detect(image)
aligned_faces = []
if faces is not None:
for face in faces:
aligned_face = face_recognizer.alignCrop(image, face)
aligned_faces.append(aligned_face)
for i, aligned_face in enumerate(aligned_faces):
cv2.imshow("aligned_face {:03}".format(i + 1), aligned_face)
cv2.imwrite(os.path.join(directory, "face{:03}.jpg".format(i + 1)), aligned_face)
cv2.waitKey(0)
cv2.destroyAllWindows()
if __name__ == '__main__':
main()
执行命令
python generate_aligned_faces.py input.jpg
输入图像(input.jpg)
执行结果
face001.jpg | face002.jpg |
---|---|
提取特征并保存为字典
从面部图像中提取特征并将其保存为特征字典。
源代码
import os
import sys
import argparse
import numpy as np
import cv2
def main():
parser = argparse.ArgumentParser("generate face feature dictionary from an face image")
parser.add_argument("image", help="input face image file path (./<face名>.jpg)")
args = parser.parse_args()
print(args.image)
path = args.image
directory = os.path.dirname(args.image)
if not directory:
directory = os.path.dirname(__file__)
path = os.path.join(directory, args.image)
image = cv2.imread(path)
if image is None:
exit()
channels = 1 if len(image.shape) == 2 else image.shape[2]
if channels == 1:
image = cv2.cvtColor(image, cv2.COLOR_GRAY2BGR)
if channels == 4:
image = cv2.cvtColor(image, cv2.COLOR_BGRA2BGR)
weights = "onnx/face_recognizer_fast.onnx"
face_recognizer = cv2.FaceRecognizerSF_create(weights, "")
face_feature = face_recognizer.feature(image)
print(face_feature)
print(type(face_feature))
basename = os.path.splitext(os.path.basename(args.image))[0]
dictionary = os.path.join(directory, basename)
np.save(dictionary, face_feature)
if __name__ == '__main__':
main()
执行命令
python generate_feature_dictionary.py face001.jpg
输入图像(为每个图像执行的源代码)
face001.jpg | face002.jpg |
---|---|
执行结果
输出face001.npy和face002.npy。
人脸识别处理
从捕获的图像中检测人脸,提取特征,并将其与特征字典进行比较以进行面部识别。
源代码
import cv2
import os
import glob
import numpy as np
from picamera2 import Picamera2
from libcamera import controls
COSINE_THRESHOLD = 0.363
NORML2_THRESHOLD = 1.128
def match(recognizer, feature1, dictionary):
for element in dictionary:
user_id, feature2 = element
score = recognizer.match(feature1, feature2, cv2.FaceRecognizerSF_FR_COSINE)
if score > COSINE_THRESHOLD:
return True, (user_id, score)
return False, ("", 0.0)
def main():
camera = Picamera2()
camera.configure(camera.create_preview_configuration(main={"format": 'XRGB8888', "size": (640, 480)}))
camera.start()
camera.set_controls({'AfMode': controls.AfModeEnum.Continuous})
dictionary = []
files = glob.glob(os.path.join("sample_data", "*.npy"))
for file in files:
feature = np.load(file)
user_id = os.path.splitext(os.path.basename(file))[0]
dictionary.append((user_id, feature))
weights = "onnx/yunet_n_640_640.onnx"
face_detector = cv2.FaceDetectorYN_create(weights, "", (0, 0))
weights = "onnx/face_recognizer_fast.onnx"
face_recognizer = cv2.FaceRecognizerSF_create(weights, "")
while True:
image = camera.capture_array()
channels = 1 if len(image.shape) == 2 else image.shape[2]
if channels == 1:
image = cv2.cvtColor(image, cv2.COLOR_GRAY2BGR)
if channels == 4:
image = cv2.cvtColor(image, cv2.COLOR_BGRA2BGR)
height, width, _ = image.shape
face_detector.setInputSize((width, height))
result, faces = face_detector.detect(image)
faces = faces if faces is not None else []
for face in faces:
aligned_face = face_recognizer.alignCrop(image, face)
feature = face_recognizer.feature(aligned_face)
result, user = match(face_recognizer, feature, dictionary)
box = list(map(int, face[:4]))
color = (0, 255, 0) if result else (0, 0, 255)
thickness = 2
cv2.rectangle(image, box, color, thickness, cv2.LINE_AA)
id, score = user if result else ("unknown", 0.0)
text = "{0} ({1:.2f})".format(id, score)
position = (box[0], box[1] - 10)
font = cv2.FONT_HERSHEY_SIMPLEX
scale = 0.6
cv2.putText(image, text, position, font, scale, color, thickness, cv2.LINE_AA)
cv2.imshow("face recognition", image)
key = cv2.waitKey(1)
if key == ord('q'):
break
camera.close()
cv2.destroyAllWindows()
if __name__ == '__main__':
main()
执行命令
python camface_recognizer.py
执行结果
使用 Streamlit 的 Web 应用程序
关于 Streamlit
Streamlit 是一个开源框架,可使用 Python 创建 Web 应用程序。它拥有丰富的UI组件,可以用简短的代码直观地编写,可以轻松地使用WebUI创建应用程序。适合小规模开发,例如演示应用程序。
安装包
安装与 Streamlit 相关的软件包。
pip install streamlit
pip install streamlit-webrtc
pip install streamlit_server_state
在 Web 应用程序屏幕上显示捕获的视频
import cv2
import os
import glob
import numpy as np
from picamera2 import Picamera2
from libcamera import controls
import streamlit as st
from streamlit_server_state import server_state, server_state_lock
def get_camera() -> Picamera2:
with server_state_lock["camera"]:
if "camera" not in server_state:
camera = Picamera2()
camera.configure(camera.create_preview_configuration(main={
"format": 'XRGB8888',
"size": (640, 480)
}))
camera.start()
camera.set_controls({'AfMode': controls.AfModeEnum.Continuous})
server_state.camera = camera
return server_state.camera
def main():
st.markdown("# Face Recognition Application")
camera = get_camera()
try:
image_loc = st.empty()
while True:
image = camera.capture_array()
channels = 1 if len(image.shape) == 2 else image.shape[2]
if channels == 1:
image = cv2.cvtColor(image, cv2.COLOR_GRAY2BGR)
if channels == 4:
image = cv2.cvtColor(image, cv2.COLOR_BGRA2BGR)
image = cv2.cvtColor(image, cv2.COLOR_RGB2BGR)
image_loc.image(image)
except KeyboardInterrupt:
print('app stop!!')
except Exception as e:
st.markdown("### 错误。")
st.markdown(f'{e}')
camera.close()
cv2.destroyAllWindows()
if __name__ == '__main__':
main()
执行命令
python run_streamlit.py
添加人脸识别处理
import cv2
import os
import glob
import numpy as np
from picamera2 import Picamera2
from libcamera import controls
import streamlit as st
from streamlit_server_state import server_state, server_state_lock
COSINE_THRESHOLD = 0.363
NORML2_THRESHOLD = 1.128
GLOB_PATH = '*********/sample_data/*.npy'
FACE_DETECTOR_WEIGHTS = '*********/onnx/yunet_n_640_640.onnx'
FACE_RECOGNIZER_WEIGHTS = '*********/onnx/face_recognizer_fast.onnx'
def match(recognizer, feature1, dictionary) -> tuple[bool,set[str,float]]:
for element in dictionary:
user_id, feature2 = element
score = recognizer.match(feature1, feature2, cv2.FaceRecognizerSF_FR_COSINE)
if score > COSINE_THRESHOLD:
return True, (user_id, score)
return False, ("", 0.0)
def get_camera() -> Picamera2:
with server_state_lock["camera"]:
if "camera" not in server_state:
camera = Picamera2()
camera.configure(camera.create_preview_configuration(main={
"format": 'XRGB8888',
"size": (640, 480)
}))
camera.start()
camera.set_controls({'AfMode': controls.AfModeEnum.Continuous})
server_state.camera = camera
return server_state.camera
def recognize_face(face_recognizer, dictionary, image, face) -> np.ndarray:
aligned_face = face_recognizer.alignCrop(image, face)
feature = face_recognizer.feature(aligned_face)
result, user = match(face_recognizer, feature, dictionary)
box = list(map(int, face[:4]))
color = (0, 255, 0) if result else (0, 0, 255)
thickness = 2
cv2.rectangle(image, box, color, thickness, cv2.LINE_AA)
id, score = user if result else ("unknown", 0.0)
text = "{0} ({1:.2f})".format(id, score)
position = (box[0], box[1] - 10)
font = cv2.FONT_HERSHEY_SIMPLEX
scale = 0.6
cv2.putText(image, text, position, font, scale, color, thickness, cv2.LINE_AA)
return image
def main():
st.markdown("# Face Recognition Application")
dictionary = []
files = glob.glob(GLOB_PATH)
for file in files:
feature = np.load(file)
user_id = os.path.splitext(os.path.basename(file))[0]
dictionary.append((user_id, feature))
face_detector = cv2.FaceDetectorYN_create(FACE_DETECTOR_WEIGHTS, "", (0, 0))
face_recognizer = cv2.FaceRecognizerSF_create(FACE_RECOGNIZER_WEIGHTS, "")
camera = get_camera()
try:
image_loc = st.empty()
while True:
image = camera.capture_array()
channels = 1 if len(image.shape) == 2 else image.shape[2]
if channels == 1:
image = cv2.cvtColor(image, cv2.COLOR_GRAY2BGR)
if channels == 4:
image = cv2.cvtColor(image, cv2.COLOR_BGRA2BGR)
height, width, _ = image.shape
face_detector.setInputSize((width, height))
result, faces = face_detector.detect(image)
faces = faces if faces is not None else []
for face in faces:
image = recognize_face(face_recognizer, dictionary,image, face)
image = cv2.cvtColor(image, cv2.COLOR_RGB2BGR)
image_loc.image(image)
except KeyboardInterrupt:
print('app stop!!')
except Exception as e:
st.markdown("### 错误")
st.markdown(f'{e}')
camera.close()
cv2.destroyAllWindows()
if __name__ == '__main__':
main()
执行命令
python run_streamlit.py
执行结果
我在 Streamlit 中偶然发现的事情
Streamlit 旨在每次用户访问应用程序时从头开始运行脚本。因此,如果在一个用户查看相机图像时另一个用户访问该应用程序,则相机的初始设置过程将重新执行,服务器将停止。我能够通过使用streamlit_server_state库在服务器内共享相机来解决这个问题,而不管会话如何。
与 AWS Rekognition 的比较视频
Amazon Rekognition 流视频事件处理来自新的或现有 Kinesis Video Streams 的视频。仅当你发送开始视频分析的通知时,Rekognition 才会开始处理你的 Kinesis Video 流,并且每个事件最多可以分析 120 秒的视频。只需为 Amazon Rekognition 处理的视频量付费。注意:Amazon Kinesis Video Streams 服务单独计费。
- Amazon Rekognition Image 没那么贵,但是Amazon Rekognition Video 比我想象的要贵。
- 如果使用Amazon Kinesis Video Streams流式传输视频,将需要支付额外的服务费。
- 视频中的人脸搜索:0.15USD / 1m
树莓派
- 如果你使用 OpenCV 等,它基本上是免费的。
- Raspberry Pi 等硬件成本是在安装时产生的。
- 由于它取决于硬件,因此可能会产生故障和运营成本等额外成本。
- 但是,Amazon Rekognition Video 还需要外部摄像头,因此需要考虑如何使用硬件。
存在一些因素,例如如何创建模型以及你想要多少精度,但是当仅考虑价格时,使用 Raspberry Pi 进行操作似乎要便宜得多。
文章来源:https://blog.csdn.net/weixin_41556756/article/details/134989230
本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。 如若内容造成侵权/违法违规/事实不符,请联系我的编程经验分享网邮箱:veading@qq.com进行投诉反馈,一经查实,立即删除!
本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。 如若内容造成侵权/违法违规/事实不符,请联系我的编程经验分享网邮箱:veading@qq.com进行投诉反馈,一经查实,立即删除!