import cv2
import numpy as np
import mediapipe as mp
import mss
import time
import logging
import threading
from dataclasses import dataclass
from typing import Tuple, Any
from queue import Queue

# 设置日志配置
logging.basicConfig(level=logging.INFO, format='%(asctime)s - %(levelname)s - %(message)s')

# 初始化 MediaPipe Pose
mp_drawing = mp.solutions.drawing_utils
mp_pose = mp.solutions.pose

# 定义颜色
class Color:
    WHITE = (255, 255, 255)
    GREEN = (0, 255, 0)
    RED = (0, 0, 255)
    BLUE = (255, 0, 0)

@dataclass
class MonitorConfig:
    """屏幕监控配置"""
    top: int = 0
    left: int = 0
    width: int = 2880  # 修改为您的屏幕宽度
    height: int = 1800  # 修改为您的屏幕高度

@dataclass
class PoseEstimatorConfig:
    """姿态估计器配置"""
    min_detection_confidence: float = 0.3  # 降低置信度阈值
    min_tracking_confidence: float = 0.3  # 降低置信度阈值

class PoseEstimator:
    def __init__(self, config: PoseEstimatorConfig):
        self.pose = mp_pose.Pose(
            min_detection_confidence=config.min_detection_confidence,
            min_tracking_confidence=config.min_tracking_confidence
        )

    def process(self, image: np.ndarray) -> Any:
        """处理图像并进行姿态检测。"""
        image_rgb = cv2.cvtColor(image, cv2.COLOR_BGR2RGB)
        image_rgb.flags.writeable = False
        results = self.pose.process(image_rgb)
        return results

    def draw(self, image: np.ndarray, results: Any):
        """在图像上绘制13个骨骼点的骨架、人物矩形框以及头部方框。"""
        if results.pose_landmarks:
            # 获取关节点坐标
            landmarks = results.pose_landmarks.landmark

            image_height, image_width, _ = image.shape

            # 定义骨骼点索引
            nose_idx = mp_pose.PoseLandmark.NOSE.value
            left_shoulder_idx = mp_pose.PoseLandmark.LEFT_SHOULDER.value
            right_shoulder_idx = mp_pose.PoseLandmark.RIGHT_SHOULDER.value
            left_elbow_idx = mp_pose.PoseLandmark.LEFT_ELBOW.value
            right_elbow_idx = mp_pose.PoseLandmark.RIGHT_ELBOW.value
            left_wrist_idx = mp_pose.PoseLandmark.LEFT_WRIST.value
            right_wrist_idx = mp_pose.PoseLandmark.RIGHT_WRIST.value
            left_hip_idx = mp_pose.PoseLandmark.LEFT_HIP.value
            right_hip_idx = mp_pose.PoseLandmark.RIGHT_HIP.value
            left_knee_idx = mp_pose.PoseLandmark.LEFT_KNEE.value
            right_knee_idx = mp_pose.PoseLandmark.RIGHT_KNEE.value
            left_ankle_idx = mp_pose.PoseLandmark.LEFT_ANKLE.value
            right_ankle_idx = mp_pose.PoseLandmark.RIGHT_ANKLE.value

            # 骨骼点字典
            keypoints = {}

            # 检查并获取每个关键点的坐标
            def get_coord(idx):
                landmark = landmarks[idx]
                if landmark.visibility > 0 and 0 <= landmark.x <= 1 and 0 <= landmark.y <=1:
                    x = int(landmark.x * image_width)
                    y = int(landmark.y * image_height)
                    return (x, y)
                else:
                    return None

            # 获取关键点坐标
            keypoints['head'] = get_coord(nose_idx)
            keypoints['left_shoulder'] = get_coord(left_shoulder_idx)
            keypoints['right_shoulder'] = get_coord(right_shoulder_idx)
            keypoints['left_elbow'] = get_coord(left_elbow_idx)
            keypoints['right_elbow'] = get_coord(right_elbow_idx)
            keypoints['left_wrist'] = get_coord(left_wrist_idx)
            keypoints['right_wrist'] = get_coord(right_wrist_idx)
            keypoints['left_hip'] = get_coord(left_hip_idx)
            keypoints['right_hip'] = get_coord(right_hip_idx)
            keypoints['left_knee'] = get_coord(left_knee_idx)
            keypoints['right_knee'] = get_coord(right_knee_idx)
            keypoints['left_ankle'] = get_coord(left_ankle_idx)
            keypoints['right_ankle'] = get_coord(right_ankle_idx)

            # 计算颈部骨骼点（左右肩膀的中点）
            if keypoints['left_shoulder'] and keypoints['right_shoulder']:
                neck_x = int((keypoints['left_shoulder'][0] + keypoints['right_shoulder'][0]) / 2)
                neck_y = int((keypoints['left_shoulder'][1] + keypoints['right_shoulder'][1]) / 2)
                keypoints['neck'] = (neck_x, neck_y)
            else:
                keypoints['neck'] = None

            # 计算盆骨骨骼点（左右臀部的中点）
            if keypoints['left_hip'] and keypoints['right_hip']:
                pelvis_x = int((keypoints['left_hip'][0] + keypoints['right_hip'][0]) / 2)
                pelvis_y = int((keypoints['left_hip'][1] + keypoints['right_hip'][1]) / 2)
                keypoints['pelvis'] = (pelvis_x, pelvis_y)
            else:
                keypoints['pelvis'] = None

            # 定义连接关系
            connections = [
                ('head', 'neck'),
                ('neck', 'left_shoulder'),
                ('neck', 'right_shoulder'),
                ('left_shoulder', 'left_elbow'),
                ('right_shoulder', 'right_elbow'),
                ('left_elbow', 'left_wrist'),
                ('right_elbow', 'right_wrist'),
                ('neck', 'pelvis'),
                ('pelvis', 'left_knee'),
                ('pelvis', 'right_knee'),
                ('left_knee', 'left_ankle'),
                ('right_knee', 'right_ankle'),
            ]

            # 绘制骨骼
            for connection in connections:
                partA, partB = connection
                if keypoints.get(partA) and keypoints.get(partB):
                    cv2.line(image, keypoints[partA], keypoints[partB], Color.GREEN, 2)

            # 绘制头部方框
            if keypoints['head']:
                head_size = 20  # 方框大小，可根据需要调整
                top_left = (keypoints['head'][0] - head_size, keypoints['head'][1] - head_size)
                bottom_right = (keypoints['head'][0] + head_size, keypoints['head'][1] + head_size)
                cv2.rectangle(image, top_left, bottom_right, Color.BLUE, 2)

            # 绘制包围人物的矩形框（基于所有可见关键点）
            x_values = [pt[0] for pt in keypoints.values() if pt]
            y_values = [pt[1] for pt in keypoints.values() if pt]

            if x_values and y_values:
                min_x = min(x_values) - 20
                max_x = max(x_values) + 20
                min_y = min(y_values) - 20
                max_y = max(y_values) + 20

                # 确保坐标在图像范围内
                min_x = max(0, min_x)
                min_y = max(0, min_y)
                max_x = min(image_width, max_x)
                max_y = min(image_height, max_y)

                cv2.rectangle(image, (min_x, min_y), (max_x, max_y), Color.RED, 2)

    def __del__(self):
        if hasattr(self, 'pose'):
            self.pose.close()

def capture_screen(monitor: MonitorConfig, frame_queue: Queue, stop_event: threading.Event):
    """捕获屏幕内容并将帧放入队列。"""
    with mss.mss() as sct:
        monitor_dict = {
            "top": monitor.top,
            "left": monitor.left,
            "width": monitor.width,
            "height": monitor.height
        }
        while not stop_event.is_set():
            screen = sct.grab(monitor_dict)
            frame = np.array(screen)
            frame = cv2.cvtColor(frame, cv2.COLOR_BGRA2BGR)

            # 将帧放入队列
            if not frame_queue.full():
                frame_queue.put(frame)
            else:
                # 丢弃旧的帧，避免队列阻塞
                frame_queue.get()
                frame_queue.put(frame)
            # 控制帧率
            time.sleep(0.01)

def calculate_fps(prev_time: float) -> Tuple[float, float]:
    """计算并返回 FPS。"""
    current_time = time.time()
    fps = 1.0 / (current_time - prev_time) if current_time - prev_time > 0 else 0.0
    return fps, current_time

def display_fps(frame: np.ndarray, fps: float):
    """在图像上显示 FPS。"""
    cv2.putText(
        frame,
        f'FPS: {fps:.2f}',
        (10, 30),
        cv2.FONT_HERSHEY_SIMPLEX,
        1,
        Color.WHITE,
        2
    )

def main():
    # 初始化配置
    monitor_config = MonitorConfig()
    estimator_config = PoseEstimatorConfig()
    frame_queue = Queue(maxsize=5)  # 限制队列大小，防止内存占用过大
    stop_event = threading.Event()

    # 初始化窗口
    window_name = "Pose Estimation"
    cv2.namedWindow(window_name, cv2.WINDOW_NORMAL | cv2.WINDOW_KEEPRATIO)
    cv2.setWindowProperty(window_name, cv2.WND_PROP_TOPMOST, 1)  # 设置窗口为最顶层

    # 初始化姿态估计器
    pose_estimator = PoseEstimator(estimator_config)

    # 启动屏幕捕获线程
    capture_thread = threading.Thread(target=capture_screen, args=(monitor_config, frame_queue, stop_event))
    capture_thread.start()

    prev_time = time.time()

    try:
        while True:
            if not frame_queue.empty():
                frame = frame_queue.get()
                # 处理图像
                results = pose_estimator.process(frame)
                # 绘制结果
                pose_estimator.draw(frame, results)
                # 计算并显示 FPS
                fps, prev_time = calculate_fps(prev_time)
                display_fps(frame, fps)
                # 显示图像，调整窗口大小以适应大分辨率
                cv2.namedWindow(window_name, cv2.WINDOW_NORMAL)
                cv2.resizeWindow(window_name, 1280, 720)  # 您可以根据需要调整窗口大小
                cv2.imshow(window_name, frame)
            # 检查是否按下了 'q' 键
            if cv2.waitKey(1) & 0xFF == ord('q'):
                logging.info("用户退出程序。")
                break
    except KeyboardInterrupt:
        logging.info("程序被手动中断。")
    except Exception as e:
        logging.exception(f"发生错误：{e}")
    finally:
        stop_event.set()
        capture_thread.join()
        cv2.destroyAllWindows()

if __name__ == "__main__":
    main()详细仔细分析代码