fix
This commit is contained in:
5
.idea/.gitignore
generated
vendored
Normal file
5
.idea/.gitignore
generated
vendored
Normal file
@@ -0,0 +1,5 @@
|
||||
# 默认忽略的文件
|
||||
/shelf/
|
||||
/workspace.xml
|
||||
# 基于编辑器的 HTTP 客户端请求
|
||||
/httpRequests/
|
||||
8
.idea/face-random.iml
generated
Normal file
8
.idea/face-random.iml
generated
Normal file
@@ -0,0 +1,8 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<module type="PYTHON_MODULE" version="4">
|
||||
<component name="NewModuleRootManager">
|
||||
<content url="file://$MODULE_DIR$" />
|
||||
<orderEntry type="jdk" jdkName="Python 3.10" jdkType="Python SDK" />
|
||||
<orderEntry type="sourceFolder" forTests="false" />
|
||||
</component>
|
||||
</module>
|
||||
6
.idea/inspectionProfiles/profiles_settings.xml
generated
Normal file
6
.idea/inspectionProfiles/profiles_settings.xml
generated
Normal file
@@ -0,0 +1,6 @@
|
||||
<component name="InspectionProjectProfileManager">
|
||||
<settings>
|
||||
<option name="USE_PROJECT_PROFILE" value="false" />
|
||||
<version value="1.0" />
|
||||
</settings>
|
||||
</component>
|
||||
7
.idea/misc.xml
generated
Normal file
7
.idea/misc.xml
generated
Normal file
@@ -0,0 +1,7 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<project version="4">
|
||||
<component name="Black">
|
||||
<option name="sdkName" value="Python 3.10" />
|
||||
</component>
|
||||
<component name="ProjectRootManager" version="2" project-jdk-name="Python 3.10" project-jdk-type="Python SDK" />
|
||||
</project>
|
||||
8
.idea/modules.xml
generated
Normal file
8
.idea/modules.xml
generated
Normal file
@@ -0,0 +1,8 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<project version="4">
|
||||
<component name="ProjectModuleManager">
|
||||
<modules>
|
||||
<module fileurl="file://$PROJECT_DIR$/.idea/face-random.iml" filepath="$PROJECT_DIR$/.idea/face-random.iml" />
|
||||
</modules>
|
||||
</component>
|
||||
</project>
|
||||
6
.idea/vcs.xml
generated
Normal file
6
.idea/vcs.xml
generated
Normal file
@@ -0,0 +1,6 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<project version="4">
|
||||
<component name="VcsDirectoryMappings">
|
||||
<mapping directory="" vcs="Git" />
|
||||
</component>
|
||||
</project>
|
||||
@@ -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):
|
||||
207
2.py
Normal file
207
2.py
Normal file
@@ -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()
|
||||
199
3.py
Normal file
199
3.py
Normal file
@@ -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()
|
||||
Reference in New Issue
Block a user