import cv2
import os
import sys
import time
from PyQt5.QtCore import Qt, QThread, QTimer, pyqtSignal
from PyQt5.QtGui import QImage, QPixmap
from PyQt5.QtWidgets import QApplication, QMainWindow, QLabel, QVBoxLayout, QWidget, QPushButton, QGridLayout, QTextEdit, QSlider, QCheckBox
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)
class VideoThread(QThread): # 确保这里使用了正确的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(300, 200, Qt.KeepAspectRatio)
self.change_pixmap_signal.emit(p)
self.frame = frame # 保存当前帧以便后续使用
time.sleep(1/30) # 限制帧率
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, 800, 600)
# 主布局
layout = QGridLayout()
# 左侧实时图像标签
self.left_label = QLabel(self)
layout.addWidget(self.left_label, 0, 0, 2, 1)
# 中间目标图案标签
self.middle_label = QLabel(self)
layout.addWidget(self.middle_label, 0, 1)
# 截图按钮
self.capture_button = QPushButton('Capture', self)
self.capture_button.clicked.connect(self.save_frame)
layout.addWidget(self.capture_button, 1, 1)
# 右侧日志文本框
self.log_text = QTextEdit(self)
layout.addWidget(self.log_text, 0, 2, 2, 1)
# 设置按钮
self.settings_button = QPushButton('Settings', self)
self.settings_button.clicked.connect(self.open_settings_window)
layout.addWidget(self.settings_button, 2, 1)
# 创建中心小部件
central_widget = QWidget()
central_widget.setLayout(layout)
self.setCentralWidget(central_widget)
# 视频线程
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()
def update_image(self, image):
self.left_label.setPixmap(QPixmap.fromImage(image))
def save_frame(self):
now = datetime.now()
timestamp = now.strftime("%Y%m%d_%H%M%S%f")
image_path = os.path.join(SAVE_PATH, f"frame_{timestamp}.jpg")
if self.thread.frame is not None:
cv2.imwrite(image_path, self.thread.frame)
self.log_event(f"Frame captured and saved to {image_path}")
def log_event(self, text):
with open(LOG_FILE, 'a') as file:
file.write(f"{datetime.now().strftime('%Y-%m-%d %H:%M:%S.%f')[:-3]} - {text}\n")
self.log_text.append(f"{datetime.now().strftime('%Y-%m-%d %H:%M:%S.%f')[:-3]} - {text}")
def on_face_detected(self):
self.log_event("Face detected")
def open_settings_window(self):
settings_window = SettingsWindow()
settings_window.show()
def closeEvent(self, event):
self.thread.stop()
event.accept()
class SettingsWindow(QWidget):
def __init__(self):
super().__init__()
self.setWindowTitle('Settings')
self.setGeometry(300, 300, 300, 200)
layout = QVBoxLayout()
self.similarity_slider = QSlider(Qt.Horizontal)
self.similarity_slider.setMinimum(0)
self.similarity_slider.setMaximum(100)
self.similarity_slider.setValue(50)
layout.addWidget(self.similarity_slider)
self.feature_detection_checkbox = QCheckBox('Detect Features')
layout.addWidget(self.feature_detection_checkbox)
self.edge_detection_checkbox = QCheckBox('Detect Edges')
layout.addWidget(self.edge_detection_checkbox)
self.setLayout(layout)
if __name__ == '__main__':
app = QApplication(sys.argv)
window = MainWindow()
# 加载级联分类器
face_cascade = cv2.CascadeClassifier(cv2.data.haarcascades + 'haarcascade_frontalface_default.xml')
window.show()
sys.exit(app.exec_())