import cv2
import os
import sys
import threading
import time
import pyttsx3
from PyQt5.QtCore import Qt, QTimer, QThread, pyqtSignal
from PyQt5.QtGui import QImage, QPixmap
from PyQt5.QtWidgets import QApplication, QMainWindow, QLabel, QVBoxLayout, QWidget, QLineEdit, QPushButton
from datetime import datetime
# 保存路径
SAVE_PATH = r"D:\Pic"
LOG_FILE = os.path.join(SAVE_PATH, "log.txt")
# 创建目录
if not os.path.exists(SAVE_PATH):
os.makedirs(SAVE_PATH)
# 初始化语音引擎
engine = pyttsx3.init()
# 新增线程类用于播报运行时间
class TimeAnnouncerThread(QThread):
def __init__(self, main_window):
super().__init__()
self.main_window = main_window
self.running = False
self.start_time = None
def run(self):
self.running = True
self.start_time = datetime.now()
while self.running:
current_time = datetime.now()
elapsed_time = current_time - self.start_time
#elapsed_time_2=elapsed_time.strftime('%Y-%m-%d %H:%M:%S')
#message = f"{self.person_name},目前已经刷牙: 0.0612"
#message = f"{self.person_name},捕捉OK. 时间是 {now.strftime('%Y-%m-%d %H:%M:%S')}" file.write(f"{datetime.now().strftime('%Y-%m-%d %H:%M:%S')} - {text}\n")
message = f"目前已经运行: 0.0612"
if self.main_window.person_name:
self.main_window.speak(message)
else:
self.main_window.speak("请输入人名")
time.sleep(10)
def stop(self):
self.running = False
self.wait()
class VideoThread(QThread):
change_pixmap_signal = pyqtSignal(QImage)
face_detected_signal = pyqtSignal() # 新增信号用于触发面部检测事件
def __init__(self):
super().__init__()
self._run_flag = True
self.frame = None
def run(self):
cap = cv2.VideoCapture(0)
if not cap.isOpened():
print("Cannot open camera")
return
while self._run_flag:
ret, frame = cap.read()
if ret:
# 面部检测
gray = cv2.cvtColor(frame, cv2.COLOR_BGR2GRAY)
faces = face_cascade.detectMultiScale(gray, scaleFactor=1.1, minNeighbors=5, minSize=(30, 30))
for (x, y, w, h) in faces:
cv2.rectangle(frame, (x, y), (x+w, y+h), (255, 0, 0), 2)
self.face_detected_signal.emit() # 触发面部检测信号
rgb_image = cv2.cvtColor(frame, cv2.COLOR_BGR2RGB)
h, w, ch = rgb_image.shape
bytes_per_line = ch * w
convert_to_Qt_format = QImage(rgb_image.data, w, h, bytes_per_line, QImage.Format_RGB888)
p = convert_to_Qt_format.scaled(640, 480, Qt.KeepAspectRatio)
self.change_pixmap_signal.emit(p)
self.frame = frame # 保存当前帧以便后续使用
time.sleep(1/30) # 限制帧率
cap.release()
def stop(self):
self._run_flag = False
self.wait()
class MainWindow(QMainWindow):
def __init__(self):
super().__init__()
self.setWindowTitle('Face Detection App')
self.setGeometry(100, 100, 900, 700)
self.label = QLabel(self)
self.label.resize(640, 480)
self.setCentralWidget(self.label)
self.thread = VideoThread()
self.thread.change_pixmap_signal.connect(self.update_image)
self.thread.face_detected_signal.connect(self.on_face_detected) # 连接面部检测信号
self.thread.start()
# 添加输入框和按钮
self.name_input = QLineEdit(self)
self.name_input.move(20, 600)
self.name_input.resize(200, 30)
self.name_input.returnPressed.connect(self.start_capture)
self.capture_button = QPushButton('Start Capture', self)
self.capture_button.move(20, 640)
self.capture_button.resize(200, 30)
self.capture_button.clicked.connect(self.start_capture)
# 保存人名
self.person_name = ""
self.capture_active = False
self.face_detected_count = 0
self.time_announcer_thread = TimeAnnouncerThread(self) # 实例化时间播报线程
def start_capture(self):
# 获取输入的人名并开始捕获
self.person_name = self.name_input.text()
self.capture_active = True
self.name_input.setEnabled(False)
self.capture_button.setEnabled(False)
def update_image(self, image):
self.label.setPixmap(QPixmap.fromImage(image))
def speak(self, text):
engine.say(text)
engine.runAndWait()
def log_event(self, text):
with open(LOG_FILE, 'a') as file:
file.write(f"{datetime.now().strftime('%Y-%m-%d %H:%M:%S')} - {text}\n")
def on_face_detected(self):
now = datetime.now()
timestamp = now.strftime("%Y%m%d_%H%M%S")
image_path = os.path.join(SAVE_PATH, f"face_{timestamp}.jpg")
# 检查 frame 是否为空
if self.thread.frame is not None and self.capture_active:
cv2.imwrite(image_path, self.thread.frame)
self.log_event(f"Face detected and saved to {image_path}")
# 播报捕捉OK信息
if self.face_detected_count < 2:
message = f"{self.person_name},捕捉OK. 时间是 {now.strftime('%Y-%m-%d %H:%M:%S')}"
self.speak(message)
self.face_detected_count += 1
# 如果已经播报过两次捕捉OK,则启动时间播报线程
if self.face_detected_count == 2 and not self.time_announcer_thread.isRunning():
self.time_announcer_thread.start()
else:
print("Warning: Frame is empty or None when trying to save.")
def closeEvent(self, event):
self.thread.stop()
self.time_announcer_thread.stop() # 停止时间播报线程
event.accept()
if __name__ == '__main__':
app = QApplication(sys.argv)
window = MainWindow()
# 在窗口显示之前提示用户输入人名
window.name_input.setFocus()
window.name_input.selectAll()
# 加载级联分类器
face_cascade = cv2.CascadeClassifier(cv2.data.haarcascades + 'haarcascade_frontalface_default.xml')
window.show()
sys.exit(app.exec_())