Опять же сидел ерунду учил На своем любимом Pyqt5 и mediapipe+cv2 написал какую то супер крутую программу для отслежки лица детального жестов и вроде как емоций Ниче не понял по документации использования гпт фиксил все ошибки а их было ну примерноо миллион Оч плохо работает но также высчитывает +- угол поворота и тп вашего лица ну головы епта картиночки Как вы можете заметить очень даже качественно выполнено все было кодик import sys import cv2 import numpy as np import mediapipe as mp import time import winsound from PyQt5.QtCore import Qt, QThread, pyqtSignal, QTimer from PyQt5.QtGui import QImage, QPixmap, QFont from PyQt5.QtWidgets import ( QApplication, QWidget, QLabel, QPushButton, QCheckBox, QVBoxLayout, QHBoxLayout, QGroupBox, QSpacerItem, QSizePolicy, QMessageBox, QFileDialog ) class VideoThread(QThread): change_pixmap_signal = pyqtSignal(QImage) update_fps_signal = pyqtSignal(float) gesture_signal = pyqtSignal(str) stats_signal = pyqtSignal(str) emotion_signal = pyqtSignal(str) def __init__(self, camera_id=0): super().__init__() self.cap = cv2.VideoCapture(camera_id) self.camera_id = camera_id self._run_flag = True self.rotation_angle = 0 self.mp_face_mesh = mp.solutions.face_mesh self.mp_hands = mp.solutions.hands self.mp_drawing = mp.solutions.drawing_utils self.face_mesh = self.mp_face_mesh.FaceMesh( max_num_faces=1, refine_landmarks=True, min_detection_confidence=0.5, min_tracking_confidence=0.5) self.hands = self.mp_hands.Hands( max_num_hands=2, min_detection_confidence=0.5, min_tracking_confidence=0.5) self.track_face = False self.track_detailed_face = False self.track_hands = False self.show_fps = False self.sound_alerts = False self.dark_theme = True self.prev_time = 0 def run(self): while self._run_flag: ret, frame = self.cap.read() if not ret: continue frame = cv2.flip(frame, 1) frame = self.rotate_frame(frame, self.rotation_angle) rgb_frame = cv2.cvtColor(frame, cv2.COLOR_BGR2RGB) results_face = None results_hands = None if self.track_face or self.track_detailed_face: results_face = self.face_mesh.process(rgb_frame) if self.track_hands: results_hands = self.hands.process(rgb_frame) h, w, _ = frame.shape gesture_text = "" emotion_text = "" faces_count = 0 hands_count = 0 hands_raised = 0 yaw, pitch, roll = 0, 0, 0 if results_face and results_face.multi_face_landmarks: faces_count = len(results_face.multi_face_landmarks) for face_landmarks in results_face.multi_face_landmarks: if self.track_face: x_coords = [int(lm.x * w) for lm in face_landmarks.landmark] y_coords = [int(lm.y * h) for lm in face_landmarks.landmark] x_min, x_max = min(x_coords), max(x_coords) y_min, y_max = min(y_coords), max(y_coords) padding_x = int((x_max - x_min) * 0.1) padding_y = int((y_max - y_min) * 0.15) x_min = max(0, x_min - padding_x) x_max = min(w, x_max + padding_x) y_min = max(0, y_min - padding_y) y_max = min(h, y_max + padding_y) cv2.rectangle(frame, (x_min, y_min), (x_max, y_max), (0, 255, 0), 2) if self.track_detailed_face: self.mp_drawing.draw_landmarks( frame, face_landmarks, self.mp_face_mesh.FACEMESH_TESSELATION, landmark_drawing_spec=None, connection_drawing_spec=mp.solutions.drawing_styles.get_default_face_mesh_tesselation_style()) emotion_text = self.detect_emotion(face_landmarks, frame) yaw, pitch, roll = self.estimate_head_pose(face_landmarks, w, h) mouth_open = self.is_mouth_open(face_landmarks, h) if mouth_open: mouth_points = [face_landmarks.landmark[i] for i in range(78, 88)] pts = np.array([(int(p.x * w), int(p.y * h)) for p in mouth_points]) cv2.polylines(frame, [pts], True, (0, 0, 255), 2) if results_hands and results_hands.multi_hand_landmarks: hands_count = len(results_hands.multi_hand_landmarks) for hand_landmarks in results_hands.multi_hand_landmarks: self.mp_drawing.draw_landmarks(frame, hand_landmarks, self.mp_hands.HAND_CONNECTIONS) wrist_y = hand_landmarks.landmark[self.mp_hands.HandLandmark.WRIST].y * h middle_finger_tip_y = hand_landmarks.landmark[self.mp_hands.HandLandmark.MIDDLE_FINGER_TIP].y * h if middle_finger_tip_y < wrist_y: hands_raised += 1 if hands_raised == 1: gesture_text = "Рука поднята " if self.sound_alerts: winsound.Beep(800, 150) elif hands_raised == 2: gesture_text = "Две руки подняты " if self.sound_alerts: winsound.Beep(1000, 150) else: gesture_text = "" if self.show_fps: curr_time = time.time() fps = 1.0 / (curr_time - self.prev_time) if self.prev_time else 0 self.prev_time = curr_time self.update_fps_signal.emit(fps) cv2.putText(frame, f'FPS: {int(fps)}', (10, 30), cv2.FONT_HERSHEY_SIMPLEX, 0.8, (50, 150, 50), 2) else: self.update_fps_signal.emit(0) stats_text = f"Лицо: {faces_count} | Руки: {hands_count} | Эмоция: {emotion_text} | Жесты: {gesture_text}" self.stats_signal.emit(stats_text) if emotion_text: cv2.putText(frame, f"Emotion: {emotion_text}", (10, 60), cv2.FONT_HERSHEY_SIMPLEX, 0.7, (80, 200, 80), 2) cv2.putText(frame, f"Incline (yaw,pitch,roll): {int(yaw)},{int(pitch)},{int(roll)}", (10, 90), cv2.FONT_HERSHEY_SIMPLEX, 0.6, (100, 255, 100), 1) h, w, ch = frame.shape bytes_per_line = ch * w qt_image = QImage(frame.data, w, h, bytes_per_line, QImage.Format_BGR888) self.change_pixmap_signal.emit(qt_image) self.gesture_signal.emit(gesture_text) self.emotion_signal.emit(emotion_text) self.cap.release() def detect_emotion(self, face_landmarks, frame): h, w, _ = frame.shape left_mouth = face_landmarks.landmark[61] right_mouth = face_landmarks.landmark[291] top_lip = face_landmarks.landmark[13] bottom_lip = face_landmarks.landmark[14] left_eyebrow = face_landmarks.landmark[70] right_eyebrow = face_landmarks.landmark[300] left_eye = face_landmarks.landmark[159] right_eye = face_landmarks.landmark[386] mouth_width = abs(right_mouth.x - left_mouth.x) mouth_height = abs(top_lip.y - bottom_lip.y) eyebrow_height = abs(left_eyebrow.y - left_eye.y) + abs(right_eyebrow.y - right_eye.y) if mouth_height / mouth_width > 0.3: return "Smiling" if eyebrow_height < 0.03: return "Default" return "" def is_mouth_open(self, face_landmarks, h): top_lip_y = face_landmarks.landmark[13].y * h bottom_lip_y = face_landmarks.landmark[14].y * h return (bottom_lip_y - top_lip_y) > 20 def estimate_head_pose(self, face_landmarks, w, h): nose = face_landmarks.landmark[1] left_eye = face_landmarks.landmark[33] right_eye = face_landmarks.landmark[263] left_mouth = face_landmarks.landmark[61] right_mouth = face_landmarks.landmark[291] nose_x, nose_y = nose.x * w, nose.y * h eye_center_x = (left_eye.x + right_eye.x) / 2 * w eye_center_y = (left_eye.y + right_eye.y) / 2 * h mouth_center_x = (left_mouth.x + right_mouth.x) / 2 * w mouth_center_y = (left_mouth.y + right_mouth.y) / 2 * h dx = nose_x - eye_center_x dy = mouth_center_y - eye_center_y yaw = dx pitch = dy roll = 0 return yaw, pitch, roll def rotate_frame(self, frame, angle): if angle == 90: return cv2.rotate(frame, cv2.ROTATE_90_CLOCKWISE) elif angle == 180: return cv2.rotate(frame, cv2.ROTATE_180) elif angle == 270: return cv2.rotate(frame, cv2.ROTATE_90_COUNTERCLOCKWISE) else: return frame def stop(self): self._run_flag = False self.wait() def switch_camera(self): self.cap.release() self.camera_id = 1 - self.camera_id self.cap = cv2.VideoCapture(self.camera_id) def rotate_camera(self): self.rotation_angle = (self.rotation_angle + 90) % 360 def save_screenshot(self, frame): filename, _ = QFileDialog.getSaveFileName( None, "Сохранить скриншот", "", "PNG Files (*.png);;JPEG Files (*.jpg)") if filename: cv2.imwrite(filename, frame) class MainWindow(QWidget): def __init__(self): super().__init__() self.setWindowTitle("zalupa 228 sigma camers") self.setFixedSize(1000, 700) self.setStyleSheet(""" QWidget { background-color: #121912; color: #88cc44; font-family: 'Segoe UI', Tahoma, Geneva, Verdana, sans-serif; } QLabel#statusLabel { font-size: 18px; font-weight: bold; color: #a0d468; min-height: 24px; } QLabel#fpsLabel { font-size: 14px; color: #559922; } QPushButton { background-color: #2f4f2f; border-radius: 8px; padding: 8px 14px; font-weight: 600; } QPushButton:hover { background-color: #3a6a3a; } QCheckBox { spacing: 10px; font-size: 15px; } """) self.image_label = QLabel() self.image_label.setFixedSize(720, 540) self.image_label.setStyleSheet("border: 2px solid #228822; border-radius: 10px;") controls_group = QGroupBox("Управление") controls_layout = QVBoxLayout() self.cb_face = QCheckBox("Отслеживание лица (простое)") self.cb_face.setChecked(True) self.cb_detailed_face = QCheckBox("Детальное лицо (mesh)") self.cb_hand = QCheckBox("Отслеживание рук") self.cb_fps = QCheckBox("Показывать FPS") self.cb_sound = QCheckBox("Звуковые оповещения") for cb in [self.cb_face, self.cb_detailed_face, self.cb_hand, self.cb_fps, self.cb_sound]: controls_layout.addWidget(cb) cb.stateChanged.connect(self.update_tracking) self.btn_switch_cam = QPushButton(" Сменить камеру") self.btn_switch_cam.clicked.connect(self.switch_camera) controls_layout.addWidget(self.btn_switch_cam) self.btn_rotate_cam = QPushButton(" Повернуть камеру на 90°") self.btn_rotate_cam.clicked.connect(self.rotate_camera) controls_layout.addWidget(self.btn_rotate_cam) self.btn_screenshot = QPushButton(" Сохранить скриншот") self.btn_screenshot.clicked.connect(self.save_screenshot) controls_layout.addWidget(self.btn_screenshot) self.gesture_label = QLabel("") self.gesture_label.setObjectName("statusLabel") self.fps_label = QLabel("") self.fps_label.setObjectName("fpsLabel") self.stats_label = QLabel("Статистика: ") self.stats_label.setObjectName("fpsLabel") controls_group.setLayout(controls_layout) main_layout = QHBoxLayout() main_layout.addWidget(self.image_label) main_layout.addWidget(controls_group) vertical_layout = QVBoxLayout() vertical_layout.addLayout(main_layout) vertical_layout.addWidget(self.gesture_label) vertical_layout.addWidget(self.stats_label) vertical_layout.addWidget(self.fps_label) vertical_layout.addSpacerItem(QSpacerItem(20, 40, QSizePolicy.Minimum, QSizePolicy.Expanding)) self.setLayout(vertical_layout) self.thread = VideoThread() self.thread.change_pixmap_signal.connect(self.update_image) self.thread.update_fps_signal.connect(self.update_fps) self.thread.gesture_signal.connect(self.update_gesture) self.thread.stats_signal.connect(self.update_stats) self.thread.start() self.update_tracking() def update_tracking(self): self.thread.track_face = self.cb_face.isChecked() self.thread.track_detailed_face = self.cb_detailed_face.isChecked() self.thread.track_hands = self.cb_hand.isChecked() self.thread.show_fps = self.cb_fps.isChecked() self.thread.sound_alerts = self.cb_sound.isChecked() def update_image(self, qt_image): self.image_label.setPixmap(QPixmap.fromImage(qt_image)) def update_fps(self, fps): if fps > 0: self.fps_label.setText(f"FPS: {int(fps)}") else: self.fps_label.setText("") def update_gesture(self, text): self.gesture_label.setText(text) def update_stats(self, text): self.stats_label.setText(text) def switch_camera(self): self.thread.switch_camera() def rotate_camera(self): self.thread.rotate_camera() def save_screenshot(self): pixmap = self.image_label.pixmap() if pixmap: filename, _ = QFileDialog.getSaveFileName( self, "Сохранить скриншот", "", "PNG Files (*.png);;JPEG Files (*.jpg)") if filename: pixmap.save(filename) def closeEvent(self, event): self.thread.stop() event.accept() if __name__ == "__main__": app = QApplication(sys.argv) window = MainWindow() window.show() sys.exit(app.exec_()) Python import sys import cv2 import numpy as np import mediapipe as mp import time import winsound from PyQt5.QtCore import Qt, QThread, pyqtSignal, QTimer from PyQt5.QtGui import QImage, QPixmap, QFont from PyQt5.QtWidgets import ( QApplication, QWidget, QLabel, QPushButton, QCheckBox, QVBoxLayout, QHBoxLayout, QGroupBox, QSpacerItem, QSizePolicy, QMessageBox, QFileDialog ) class VideoThread(QThread): change_pixmap_signal = pyqtSignal(QImage) update_fps_signal = pyqtSignal(float) gesture_signal = pyqtSignal(str) stats_signal = pyqtSignal(str) emotion_signal = pyqtSignal(str) def __init__(self, camera_id=0): super().__init__() self.cap = cv2.VideoCapture(camera_id) self.camera_id = camera_id self._run_flag = True self.rotation_angle = 0 self.mp_face_mesh = mp.solutions.face_mesh self.mp_hands = mp.solutions.hands self.mp_drawing = mp.solutions.drawing_utils self.face_mesh = self.mp_face_mesh.FaceMesh( max_num_faces=1, refine_landmarks=True, min_detection_confidence=0.5, min_tracking_confidence=0.5) self.hands = self.mp_hands.Hands( max_num_hands=2, min_detection_confidence=0.5, min_tracking_confidence=0.5) self.track_face = False self.track_detailed_face = False self.track_hands = False self.show_fps = False self.sound_alerts = False self.dark_theme = True self.prev_time = 0 def run(self): while self._run_flag: ret, frame = self.cap.read() if not ret: continue frame = cv2.flip(frame, 1) frame = self.rotate_frame(frame, self.rotation_angle) rgb_frame = cv2.cvtColor(frame, cv2.COLOR_BGR2RGB) results_face = None results_hands = None if self.track_face or self.track_detailed_face: results_face = self.face_mesh.process(rgb_frame) if self.track_hands: results_hands = self.hands.process(rgb_frame) h, w, _ = frame.shape gesture_text = "" emotion_text = "" faces_count = 0 hands_count = 0 hands_raised = 0 yaw, pitch, roll = 0, 0, 0 if results_face and results_face.multi_face_landmarks: faces_count = len(results_face.multi_face_landmarks) for face_landmarks in results_face.multi_face_landmarks: if self.track_face: x_coords = [int(lm.x * w) for lm in face_landmarks.landmark] y_coords = [int(lm.y * h) for lm in face_landmarks.landmark] x_min, x_max = min(x_coords), max(x_coords) y_min, y_max = min(y_coords), max(y_coords) padding_x = int((x_max - x_min) * 0.1) padding_y = int((y_max - y_min) * 0.15) x_min = max(0, x_min - padding_x) x_max = min(w, x_max + padding_x) y_min = max(0, y_min - padding_y) y_max = min(h, y_max + padding_y) cv2.rectangle(frame, (x_min, y_min), (x_max, y_max), (0, 255, 0), 2) if self.track_detailed_face: self.mp_drawing.draw_landmarks( frame, face_landmarks, self.mp_face_mesh.FACEMESH_TESSELATION, landmark_drawing_spec=None, connection_drawing_spec=mp.solutions.drawing_styles.get_default_face_mesh_tesselation_style()) emotion_text = self.detect_emotion(face_landmarks, frame) yaw, pitch, roll = self.estimate_head_pose(face_landmarks, w, h) mouth_open = self.is_mouth_open(face_landmarks, h) if mouth_open: mouth_points = [face_landmarks.landmark[i] for i in range(78, 88)] pts = np.array([(int(p.x * w), int(p.y * h)) for p in mouth_points]) cv2.polylines(frame, [pts], True, (0, 0, 255), 2) if results_hands and results_hands.multi_hand_landmarks: hands_count = len(results_hands.multi_hand_landmarks) for hand_landmarks in results_hands.multi_hand_landmarks: self.mp_drawing.draw_landmarks(frame, hand_landmarks, self.mp_hands.HAND_CONNECTIONS) wrist_y = hand_landmarks.landmark[self.mp_hands.HandLandmark.WRIST].y * h middle_finger_tip_y = hand_landmarks.landmark[self.mp_hands.HandLandmark.MIDDLE_FINGER_TIP].y * h if middle_finger_tip_y < wrist_y: hands_raised += 1 if hands_raised == 1: gesture_text = "Рука поднята " if self.sound_alerts: winsound.Beep(800, 150) elif hands_raised == 2: gesture_text = "Две руки подняты " if self.sound_alerts: winsound.Beep(1000, 150) else: gesture_text = "" if self.show_fps: curr_time = time.time() fps = 1.0 / (curr_time - self.prev_time) if self.prev_time else 0 self.prev_time = curr_time self.update_fps_signal.emit(fps) cv2.putText(frame, f'FPS: {int(fps)}', (10, 30), cv2.FONT_HERSHEY_SIMPLEX, 0.8, (50, 150, 50), 2) else: self.update_fps_signal.emit(0) stats_text = f"Лицо: {faces_count} | Руки: {hands_count} | Эмоция: {emotion_text} | Жесты: {gesture_text}" self.stats_signal.emit(stats_text) if emotion_text: cv2.putText(frame, f"Emotion: {emotion_text}", (10, 60), cv2.FONT_HERSHEY_SIMPLEX, 0.7, (80, 200, 80), 2) cv2.putText(frame, f"Incline (yaw,pitch,roll): {int(yaw)},{int(pitch)},{int(roll)}", (10, 90), cv2.FONT_HERSHEY_SIMPLEX, 0.6, (100, 255, 100), 1) h, w, ch = frame.shape bytes_per_line = ch * w qt_image = QImage(frame.data, w, h, bytes_per_line, QImage.Format_BGR888) self.change_pixmap_signal.emit(qt_image) self.gesture_signal.emit(gesture_text) self.emotion_signal.emit(emotion_text) self.cap.release() def detect_emotion(self, face_landmarks, frame): h, w, _ = frame.shape left_mouth = face_landmarks.landmark[61] right_mouth = face_landmarks.landmark[291] top_lip = face_landmarks.landmark[13] bottom_lip = face_landmarks.landmark[14] left_eyebrow = face_landmarks.landmark[70] right_eyebrow = face_landmarks.landmark[300] left_eye = face_landmarks.landmark[159] right_eye = face_landmarks.landmark[386] mouth_width = abs(right_mouth.x - left_mouth.x) mouth_height = abs(top_lip.y - bottom_lip.y) eyebrow_height = abs(left_eyebrow.y - left_eye.y) + abs(right_eyebrow.y - right_eye.y) if mouth_height / mouth_width > 0.3: return "Smiling" if eyebrow_height < 0.03: return "Default" return "" def is_mouth_open(self, face_landmarks, h): top_lip_y = face_landmarks.landmark[13].y * h bottom_lip_y = face_landmarks.landmark[14].y * h return (bottom_lip_y - top_lip_y) > 20 def estimate_head_pose(self, face_landmarks, w, h): nose = face_landmarks.landmark[1] left_eye = face_landmarks.landmark[33] right_eye = face_landmarks.landmark[263] left_mouth = face_landmarks.landmark[61] right_mouth = face_landmarks.landmark[291] nose_x, nose_y = nose.x * w, nose.y * h eye_center_x = (left_eye.x + right_eye.x) / 2 * w eye_center_y = (left_eye.y + right_eye.y) / 2 * h mouth_center_x = (left_mouth.x + right_mouth.x) / 2 * w mouth_center_y = (left_mouth.y + right_mouth.y) / 2 * h dx = nose_x - eye_center_x dy = mouth_center_y - eye_center_y yaw = dx pitch = dy roll = 0 return yaw, pitch, roll def rotate_frame(self, frame, angle): if angle == 90: return cv2.rotate(frame, cv2.ROTATE_90_CLOCKWISE) elif angle == 180: return cv2.rotate(frame, cv2.ROTATE_180) elif angle == 270: return cv2.rotate(frame, cv2.ROTATE_90_COUNTERCLOCKWISE) else: return frame def stop(self): self._run_flag = False self.wait() def switch_camera(self): self.cap.release() self.camera_id = 1 - self.camera_id self.cap = cv2.VideoCapture(self.camera_id) def rotate_camera(self): self.rotation_angle = (self.rotation_angle + 90) % 360 def save_screenshot(self, frame): filename, _ = QFileDialog.getSaveFileName( None, "Сохранить скриншот", "", "PNG Files (*.png);;JPEG Files (*.jpg)") if filename: cv2.imwrite(filename, frame) class MainWindow(QWidget): def __init__(self): super().__init__() self.setWindowTitle("zalupa 228 sigma camers") self.setFixedSize(1000, 700) self.setStyleSheet(""" QWidget { background-color: #121912; color: #88cc44; font-family: 'Segoe UI', Tahoma, Geneva, Verdana, sans-serif; } QLabel#statusLabel { font-size: 18px; font-weight: bold; color: #a0d468; min-height: 24px; } QLabel#fpsLabel { font-size: 14px; color: #559922; } QPushButton { background-color: #2f4f2f; border-radius: 8px; padding: 8px 14px; font-weight: 600; } QPushButton:hover { background-color: #3a6a3a; } QCheckBox { spacing: 10px; font-size: 15px; } """) self.image_label = QLabel() self.image_label.setFixedSize(720, 540) self.image_label.setStyleSheet("border: 2px solid #228822; border-radius: 10px;") controls_group = QGroupBox("Управление") controls_layout = QVBoxLayout() self.cb_face = QCheckBox("Отслеживание лица (простое)") self.cb_face.setChecked(True) self.cb_detailed_face = QCheckBox("Детальное лицо (mesh)") self.cb_hand = QCheckBox("Отслеживание рук") self.cb_fps = QCheckBox("Показывать FPS") self.cb_sound = QCheckBox("Звуковые оповещения") for cb in [self.cb_face, self.cb_detailed_face, self.cb_hand, self.cb_fps, self.cb_sound]: controls_layout.addWidget(cb) cb.stateChanged.connect(self.update_tracking) self.btn_switch_cam = QPushButton(" Сменить камеру") self.btn_switch_cam.clicked.connect(self.switch_camera) controls_layout.addWidget(self.btn_switch_cam) self.btn_rotate_cam = QPushButton(" Повернуть камеру на 90°") self.btn_rotate_cam.clicked.connect(self.rotate_camera) controls_layout.addWidget(self.btn_rotate_cam) self.btn_screenshot = QPushButton(" Сохранить скриншот") self.btn_screenshot.clicked.connect(self.save_screenshot) controls_layout.addWidget(self.btn_screenshot) self.gesture_label = QLabel("") self.gesture_label.setObjectName("statusLabel") self.fps_label = QLabel("") self.fps_label.setObjectName("fpsLabel") self.stats_label = QLabel("Статистика: ") self.stats_label.setObjectName("fpsLabel") controls_group.setLayout(controls_layout) main_layout = QHBoxLayout() main_layout.addWidget(self.image_label) main_layout.addWidget(controls_group) vertical_layout = QVBoxLayout() vertical_layout.addLayout(main_layout) vertical_layout.addWidget(self.gesture_label) vertical_layout.addWidget(self.stats_label) vertical_layout.addWidget(self.fps_label) vertical_layout.addSpacerItem(QSpacerItem(20, 40, QSizePolicy.Minimum, QSizePolicy.Expanding)) self.setLayout(vertical_layout) self.thread = VideoThread() self.thread.change_pixmap_signal.connect(self.update_image) self.thread.update_fps_signal.connect(self.update_fps) self.thread.gesture_signal.connect(self.update_gesture) self.thread.stats_signal.connect(self.update_stats) self.thread.start() self.update_tracking() def update_tracking(self): self.thread.track_face = self.cb_face.isChecked() self.thread.track_detailed_face = self.cb_detailed_face.isChecked() self.thread.track_hands = self.cb_hand.isChecked() self.thread.show_fps = self.cb_fps.isChecked() self.thread.sound_alerts = self.cb_sound.isChecked() def update_image(self, qt_image): self.image_label.setPixmap(QPixmap.fromImage(qt_image)) def update_fps(self, fps): if fps > 0: self.fps_label.setText(f"FPS: {int(fps)}") else: self.fps_label.setText("") def update_gesture(self, text): self.gesture_label.setText(text) def update_stats(self, text): self.stats_label.setText(text) def switch_camera(self): self.thread.switch_camera() def rotate_camera(self): self.thread.rotate_camera() def save_screenshot(self): pixmap = self.image_label.pixmap() if pixmap: filename, _ = QFileDialog.getSaveFileName( self, "Сохранить скриншот", "", "PNG Files (*.png);;JPEG Files (*.jpg)") if filename: pixmap.save(filename) def closeEvent(self, event): self.thread.stop() event.accept() if __name__ == "__main__": app = QApplication(sys.argv) window = MainWindow() window.show() sys.exit(app.exec_()) GITHUB работает ну я тестил на 3.9.10 версии requirements.txt opencv-python>=4.5.5 mediapipe>=0.8.10 PyQt5>=5.15.4 numpy>=1.21.0 СПАСЕБА ЗА ВНИМАНИЕ НА ЭТОМ СОБСТВЕННО ВСЕЕ