diff --git a/.idea/.gitignore b/.idea/.gitignore new file mode 100644 index 0000000..10b731c --- /dev/null +++ b/.idea/.gitignore @@ -0,0 +1,5 @@ +# 默认忽略的文件 +/shelf/ +/workspace.xml +# 基于编辑器的 HTTP 客户端请求 +/httpRequests/ diff --git a/.idea/face-random.iml b/.idea/face-random.iml new file mode 100644 index 0000000..8437fe6 --- /dev/null +++ b/.idea/face-random.iml @@ -0,0 +1,8 @@ + + + + + + + + \ No newline at end of file diff --git a/.idea/inspectionProfiles/profiles_settings.xml b/.idea/inspectionProfiles/profiles_settings.xml new file mode 100644 index 0000000..105ce2d --- /dev/null +++ b/.idea/inspectionProfiles/profiles_settings.xml @@ -0,0 +1,6 @@ + + + + \ No newline at end of file diff --git a/.idea/misc.xml b/.idea/misc.xml new file mode 100644 index 0000000..9de2865 --- /dev/null +++ b/.idea/misc.xml @@ -0,0 +1,7 @@ + + + + + + \ No newline at end of file diff --git a/.idea/modules.xml b/.idea/modules.xml new file mode 100644 index 0000000..b81af9f --- /dev/null +++ b/.idea/modules.xml @@ -0,0 +1,8 @@ + + + + + + + + \ No newline at end of file diff --git a/.idea/vcs.xml b/.idea/vcs.xml new file mode 100644 index 0000000..35eb1dd --- /dev/null +++ b/.idea/vcs.xml @@ -0,0 +1,6 @@ + + + + + + \ No newline at end of file diff --git a/main.py b/1.py similarity index 97% rename from main.py rename to 1.py index 8c6f004..429760a 100644 --- a/main.py +++ b/1.py @@ -2,9 +2,9 @@ import sys import random import cv2 from cvzone.FaceDetectionModule import FaceDetector -from PyQt6.QtCore import QTimer -from PyQt6.QtGui import QImage, QPixmap -from PyQt6.QtWidgets import QApplication, QWidget, QLabel, QPushButton +from PySide6.QtCore import QTimer +from PySide6.QtGui import QImage, QPixmap +from PySide6.QtWidgets import QApplication, QWidget, QLabel, QPushButton class FaceRandomApp(QWidget): diff --git a/2.py b/2.py new file mode 100644 index 0000000..d5b9bbb --- /dev/null +++ b/2.py @@ -0,0 +1,207 @@ +import cv2 +import numpy as np +import random +import os +import sys +from collections import deque + +class FaceSelector: + def __init__(self): + # 获取模型文件的正确路径 + cascade_path = self.get_cascade_path() + self.face_cascade = cv2.CascadeClassifier(cascade_path) + + # 检查模型是否加载成功 + if self.face_cascade.empty(): + fallback_path = cv2.data.haarcascades + 'haarcascade_frontalface_default.xml' + self.face_cascade = cv2.CascadeClassifier(fallback_path) + + if self.face_cascade.empty(): + sys.exit(1) + + # 状态变量 + self.random_mode = False + self.selected_face = None + self.static_frame = None + self.static_faces = None + + # 用于平滑检测的队列 + self.face_queue = deque(maxlen=5) + + # 创建窗口 + self.window_name = "Face Selector" + cv2.namedWindow(self.window_name, cv2.WINDOW_NORMAL) + + # 添加窗口关闭事件处理 + self.running = True + + def get_cascade_path(self): + """获取模型文件的正确路径""" + if getattr(sys, 'frozen', False): + base_path = sys._MEIPASS + else: + base_path = os.path.dirname(os.path.abspath(__file__)) + + possible_paths = [ + os.path.join(base_path, 'haarcascade_frontalface_default.xml'), + os.path.join(base_path, 'cv2', 'data', 'haarcascade_frontalface_default.xml'), + os.path.join(base_path, 'Library', 'etc', 'haarcascades', 'haarcascade_frontalface_default.xml'), + cv2.data.haarcascades + 'haarcascade_frontalface_default.xml' + ] + + for path in possible_paths: + if os.path.isfile(path): + return path + + return possible_paths[0] + + def detect_faces(self, frame): + """检测人脸并返回位置""" + gray = cv2.cvtColor(frame, cv2.COLOR_BGR2GRAY) + + faces = self.face_cascade.detectMultiScale( + gray, + scaleFactor=1.05, + minNeighbors=6, + minSize=(40, 40), + flags=cv2.CASCADE_SCALE_IMAGE + ) + return faces + + def draw_faces(self, frame, faces): + """在帧上绘制人脸框""" + # 在随机模式下,只绘制选中的红框 + if self.random_mode and self.selected_face is not None and self.static_faces is not None: + if self.selected_face < len(self.static_faces): + x, y, w, h = self.static_faces[self.selected_face] + cv2.rectangle(frame, (x, y), (x+w, y+h), (0, 0, 255), 3) # 选中的为红色 + else: + # 在正常模式下,绘制所有人脸框 + for (x, y, w, h) in faces: + cv2.rectangle(frame, (x, y), (x+w, y+h), (0, 255, 0), 2) # 绿色 + + return frame + + def draw_button(self, frame): + """绘制按钮""" + button_text = "Reset" if self.random_mode else "Random" + button_color = (0, 0, 255) if self.random_mode else (0, 255, 0) + + # 绘制按钮背景 + cv2.rectangle(frame, (10, 10), (150, 50), button_color, -1) + cv2.rectangle(frame, (10, 10), (150, 50), (255, 255, 255), 2) + + # 绘制按钮文字 + cv2.putText(frame, button_text, (20, 35), cv2.FONT_HERSHEY_SIMPLEX, 0.7, (255, 255, 255), 2) + + return frame + + def toggle_random_mode(self, frame, faces): + """切换随机模式""" + if not self.random_mode: + # 进入随机模式 + self.random_mode = True + if len(faces) > 0: + self.selected_face = random.randint(0, len(faces)-1) + self.static_frame = frame.copy() + self.static_faces = faces.copy() # 保存静态状态下的人脸位置 + else: + self.selected_face = None + self.static_frame = None + self.static_faces = None + else: + # 退出随机模式 + self.random_mode = False + self.selected_face = None + self.static_frame = None + self.static_faces = None + + def process_frame(self, frame): + """处理每一帧""" + # 检测人脸 + faces = self.detect_faces(frame) + + # 更新人脸队列 + if len(faces) > 0: + self.face_queue.append(faces) + + # 使用队列中最新的人脸数据 + current_faces = faces + if self.face_queue: + current_faces = self.face_queue[-1] + + # 处理随机模式 + if self.random_mode: + if self.static_frame is not None: + # 使用静态帧,只绘制选中的红框 + display_frame = self.static_frame.copy() + display_frame = self.draw_faces(display_frame, current_faces) + else: + display_frame = frame.copy() + else: + # 在实时帧上绘制所有人脸 + display_frame = self.draw_faces(frame.copy(), current_faces) + + # 绘制按钮 + display_frame = self.draw_button(display_frame) + + return display_frame + + def run(self): + """运行主程序""" + cap = cv2.VideoCapture(0) + + if not cap.isOpened(): + return + + # 获取摄像头分辨率并设置窗口大小 + ret, frame = cap.read() + if ret: + height, width = frame.shape[:2] + cv2.resizeWindow(self.window_name, width, height) + + while self.running: + ret, frame = cap.read() + if not ret: + cv2.waitKey(1000) + continue + + # 处理帧 + display_frame = self.process_frame(frame) + + # 显示结果 + cv2.imshow(self.window_name, display_frame) + + # 处理鼠标点击 + def mouse_callback(event, x, y, flags, param): + if event == cv2.EVENT_LBUTTONDOWN: + # 检查是否点击了按钮区域 + if 10 <= x <= 150 and 10 <= y <= 50: + faces = self.detect_faces(frame) + self.toggle_random_mode(frame, faces) + + cv2.setMouseCallback(self.window_name, mouse_callback) + + # 处理键盘输入和窗口关闭事件 + key = cv2.waitKey(1) & 0xFF + if key == ord('q') or key == 27: # 'q' 或 ESC 键退出 + self.running = False + break + + # 检查窗口是否被关闭 + try: + if cv2.getWindowProperty(self.window_name, cv2.WND_PROP_VISIBLE) <= 0: + self.running = False + break + except: + self.running = False + break + + # 释放资源 + cap.release() + cv2.destroyAllWindows() + sys.exit(0) + +if __name__ == "__main__": + app = FaceSelector() + app.run() diff --git a/3.py b/3.py new file mode 100644 index 0000000..e4017a5 --- /dev/null +++ b/3.py @@ -0,0 +1,199 @@ +import sys +import random +import cv2 +from cvzone.FaceDetectionModule import FaceDetector +from PySide6.QtCore import QTimer +from PySide6.QtGui import QImage, QPixmap +from PySide6.QtWidgets import QApplication, QWidget, QLabel, QPushButton + + +class FaceRandomApp(QWidget): + def __init__(self): + super().__init__() + + self.setWindowTitle("Face Random Selector") + + # 状态:normal → random + self.state = "normal" + self.selected_face_index = -1 + self.static_frame = None # 保存随机状态下的静态画面 + self.all_faces_snapshot = [] # 保存所有人脸信息快照 + + # ---------------- 摄像头 ---------------- + self.cap = cv2.VideoCapture(0) + + # 设置摄像头分辨率 + self.cap.set(3, 1280) + self.cap.set(4, 720) + + # 设置窗口初始大小 + self.resize(1280, 720) + + # 使用长距离模型和更低的人脸检测阈值以提高检测率 + self.detector = FaceDetector(minDetectionCon=0.25, modelSelection=1) + + # ---------------- 创建界面 ---------------- + self.setup_ui() + + self.faces = [] + + def setup_ui(self): + # 视频标签 - 填充整个窗口 + self.video_label = QLabel(self) + self.video_label.setStyleSheet("background:#000;") + self.video_label.setGeometry(0, 0, self.width(), self.height()) + + # 按钮 - 叠加在视频上 + self.btn = QPushButton("随机", self) + self.btn.setFixedSize(140, 55) + self.btn.setStyleSheet(""" + QPushButton { + background-color: rgba(0, 0, 0, 120); + color: white; + font-size: 20px; + padding: 10px 20px; + border-radius: 8px; + } + QPushButton:hover { + background-color: rgba(30, 30, 30, 180); + } + """) + self.btn.clicked.connect(self.button_clicked) + + # 初始按钮位置 + self.update_button_position() + + def resizeEvent(self, event): + """窗口大小改变时自动调整视频和按钮位置""" + self.video_label.setGeometry(0, 0, self.width(), self.height()) + self.update_button_position() + super().resizeEvent(event) + + def update_button_position(self): + """更新按钮位置""" + btn_w = 140 + btn_h = 55 + margin = 20 + self.btn.setGeometry( + margin, + self.height() - btn_h - margin, + btn_w, + btn_h + ) + + # --------------------------------------------------------- + # 逻辑按钮:随机 ↔ 重置 + # --------------------------------------------------------- + def button_clicked(self): + if self.state == "normal": + # 切换到随机状态 + self.state = "random" + self.btn.setText("重置") + + # 随机选择一个人脸 + if len(self.faces) > 0: + self.selected_face_index = random.randint(0, len(self.faces) - 1) + + # 保存当前帧作为静态画面 + ret, img = self.cap.read() + if ret: + img = cv2.flip(img, 1) + # 调整图像大小以适应窗口 + img = cv2.resize(img, (self.video_label.width(), self.video_label.height())) + self.static_frame = img.copy() + self.all_faces_snapshot = self.faces.copy() + else: # self.state == "random" + # 切换回初始状态 + self.state = "normal" + self.selected_face_index = -1 + self.static_frame = None + self.all_faces_snapshot = [] + self.btn.setText("随机") + + # --------------------------------------------------------- + # 刷视频帧 + # --------------------------------------------------------- + def update_frame(self): + # ============================ + # 状态:normal(检测所有人脸并显示绿框) + # ============================ + if self.state == "normal": + ret, img = self.cap.read() + if not ret: + return + + img = cv2.flip(img, 1) + + # 调整图像大小以适应窗口 + img = cv2.resize(img, (self.video_label.width(), self.video_label.height())) + + # 检测人脸 + img, faces = self.detector.findFaces(img, draw=False) + self.faces = faces if faces else [] + + # 绘制所有人脸为绿色 + for face in self.faces: + x, y, w, h = face["bbox"] + score = face["score"][0] + cv2.rectangle(img, (x, y), (x + w, y + h), (0, 255, 0), 3) + cv2.putText(img, f"{score:.2f}", (x, y - 10), + cv2.FONT_HERSHEY_SIMPLEX, 0.6, (0, 255, 0), 2) + + # 显示图像 + self.display_image(img) + + # ============================ + # 状态:random(显示静态画面,选中的人脸为红色,其他为绿色) + # ============================ + elif self.state == "random" and self.static_frame is not None: + img = self.static_frame.copy() + + # 绘制所有人脸,选中的为红色,其他为绿色 + for i, face in enumerate(self.all_faces_snapshot): + x, y, w, h = face["bbox"] + score = face["score"][0] + + if i == self.selected_face_index: + color = (0, 0, 255) # 红色 + else: + color = (0, 255, 0) # 绿色 + + cv2.rectangle(img, (x, y), (x + w, y + h), color, 3) + cv2.putText(img, f"{score:.2f}", (x, y - 10), + cv2.FONT_HERSHEY_SIMPLEX, 0.6, color, 2) + + # 显示图像 + self.display_image(img) + + # --------------------------------------------------------- + # 显示图像到 QLabel + # --------------------------------------------------------- + def display_image(self, img): + rgb = cv2.cvtColor(img, cv2.COLOR_BGR2RGB) + h, w, ch = rgb.shape + bytes_per_line = ch * w + qimg = QImage(rgb.data, w, h, bytes_per_line, QImage.Format.Format_RGB888) + self.video_label.setPixmap(QPixmap.fromImage(qimg)) + + # --------------------------------------------------------- + def closeEvent(self, event): + if self.cap.isOpened(): + self.cap.release() + event.accept() + + +def main(): + app = QApplication(sys.argv) + window = FaceRandomApp() + window.show() + + # 启动定时器 + timer = QTimer() + timer.timeout.connect(window.update_frame) + timer.start(30) + + sys.exit(app.exec()) + + +if __name__ == "__main__": + main() diff --git a/readme.md b/readme.md new file mode 100644 index 0000000..7b660a8 --- /dev/null +++ b/readme.md @@ -0,0 +1 @@ +nuitka --standalone --include-package=cv2 --enable-plugin=pyside6 --include-data-dir=F:\python310\lib\site-packages\cv2\data=cv2\data --output-dir=dist3 --remove-output 3.py \ No newline at end of file