Загрузка...

Python Software to track face + hands

Thread in Your projects created by rasez Jun 21, 2025. 171 view

The poll

аценку

Your vote will be publicly visible.
  1. имба

    2
    66.7%
  2. какаха

    0
    0%
  3. 50/50

    0
    0%
  4. сойдет

    1
    33.3%
  1. rasez
    rasez Topic starter Jun 21, 2025 Стим аккаунты тут - lolz.live/threads/7680775 :da: 1487 Apr 29, 2025
    Опять же сидел ерунду учил :da:
    На своем любимом Pyqt5 и mediapipe+cv2 написал какую то супер крутую программу для отслежки
    лица детального жестов и вроде как емоций
    :shreklol:

    Ниче не понял по документации использования
    гпт фиксил все ошибки а их было ну примерноо миллион :kakashka: :kakashka:
    Оч плохо работает но также высчитывает +- угол поворота и тп вашего лица ну головы епта
    :colobok_cool:
    [IMG]
    Как вы можете заметить очень даже качественно выполнено все было :fap:
    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 версии :colobok_amazed:

    opencv-python>=4.5.5
    mediapipe>=0.8.10
    PyQt5>=5.15.4

    numpy>=1.21.0
    СПАСЕБА ЗА ВНИМАНИЕ НА ЭТОМ СОБСТВЕННО ВСЕЕ :colobok_hi:
     
  2. krisssss
    krisssss Jun 23, 2025 Banned 10,787 Dec 24, 2024
    да для слежки за сотрудниками хорошая программа)
     
    1. rasez Topic starter
      krisssss, вово чтоб не втыкали
Loading...
Top