You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

摄像头读取循环事件计时优化:膝部姿态检测视频冻结问题

解决腿部姿态检测计时时视频冻结的问题

你的代码中嵌套的while循环是导致视频冻结的核心原因——主循环本应逐帧处理视频、渲染画面,但嵌套循环会让程序卡在计时逻辑里8秒,期间完全不处理新的视频帧,自然会出现画面冻结。

改用状态跟踪的方式实现计时逻辑,让计时和帧处理在同一个主循环里完成,就能避免阻塞。下面是修改后的完整代码:

import cv2
import numpy as np
import mediapipe as mp
from datetime import datetime

mp_drawing = mp.solutions.drawing_utils
mp_pose = mp.solutions.pose

# 读取视频
cap = cv2.VideoCapture('C:/Users/ausaf/Downloads/KneeBendVideo.mp4')

counter = 0
stage = 'straight knee'  # 初始状态设为伸直
is_timing = False  # 是否正在计时
start_timing = None
elapsed_time = 0.0

def calculate_angle(h, k, a):  # h=hip, k=knee, a=ankle
    h = np.array(h)
    k = np.array(k)
    a = np.array(a)

    radians = np.arctan2(h[1] - k[1], a[0] - k[0]) - np.arctan2(h[1] - k[1], h[0] - k[0])
    angle = np.abs(radians * 180.0 / np.pi)

    if angle > 180:
        angle = 360 - angle
    return angle

with mp_pose.Pose(min_detection_confidence=0.8, min_tracking_confidence=0.5) as pose:
    while cap.isOpened():
        ret, frame = cap.read()
        if not ret:
            break  # 视频结束退出循环

        # 颜色空间转换与姿态检测
        image = cv2.cvtColor(frame, cv2.COLOR_BGR2RGB)
        image.flags.writeable = False
        results = pose.process(image)
        image.flags.writeable = True
        image = cv2.cvtColor(image, cv2.COLOR_RGB2BGR)

        angle = None
        # 提取关键点并计算角度
        try:
            landmarks = results.pose_landmarks.landmark
            hip = [landmarks[mp_pose.PoseLandmark.LEFT_HIP.value].x, landmarks[mp_pose.PoseLandmark.LEFT_HIP.value].y]
            knee = [landmarks[mp_pose.PoseLandmark.LEFT_KNEE.value].x, landmarks[mp_pose.PoseLandmark.LEFT_KNEE.value].y]
            ankle = [landmarks[mp_pose.PoseLandmark.LEFT_ANKLE.value].x, landmarks[mp_pose.PoseLandmark.LEFT_ANKLE.value].y]

            angle = calculate_angle(hip, knee, ankle)
            # 显示角度
            cv2.putText(image, f"{int(angle)}°",
                        tuple(np.multiply(knee, [854, 640]).astype(int)),
                        cv2.FONT_HERSHEY_SIMPLEX, 0.5, (255, 255, 255), 2, cv2.LINE_AA)
        except:
            pass

        # 渲染计数面板
        cv2.rectangle(image, (0, 0), (225, 73), (245, 117, 16), -1)
        cv2.putText(image, 'REPS', (15, 12),
                    cv2.FONT_HERSHEY_SIMPLEX, 0.5, (0, 0, 0), 1, cv2.LINE_AA)
        cv2.putText(image, str(counter), (10, 60),
                    cv2.FONT_HERSHEY_SIMPLEX, 2, (255, 255, 255), 2, cv2.LINE_AA)

        cv2.putText(image, 'Knee Status', (65, 12),
                    cv2.FONT_HERSHEY_SIMPLEX, 0.5, (0, 0, 0), 1, cv2.LINE_AA)
        cv2.putText(image, stage, (60, 60),
                    cv2.FONT_HERSHEY_SIMPLEX, 2, (255, 255, 255), 2, cv2.LINE_AA)

        # 渲染姿态关键点
        mp_drawing.draw_landmarks(image, results.pose_landmarks, mp_pose.POSE_CONNECTIONS,
                                  mp_drawing.DrawingSpec(color=(245, 117, 66), thickness=2, circle_radius=2),
                                  mp_drawing.DrawingSpec(color=(245, 66, 230), thickness=2, circle_radius=2))

        # 核心计时与计数逻辑(无阻塞)
        if angle is not None:
            if angle > 140:
                stage = 'straight knee'
                if is_timing:
                    # 计时未完成就伸直,提示用户
                    cv2.putText(image, 'Keep your knees bent!', (50, 100),
                                cv2.FONT_HERSHEY_SIMPLEX, 1, (0, 0, 255), 2, cv2.LINE_AA)
                    is_timing = False  # 停止计时

            elif angle < 140:
                if stage == 'straight knee' and not is_timing:
                    # 刚进入弯曲状态,启动计时
                    is_timing = True
                    start_timing = datetime.now()
                    stage = 'bent knee'

                if is_timing:
                    # 计算已流逝时间
                    elapsed_time = (datetime.now() - start_timing).total_seconds()
                    # 显示剩余时间
                    remaining_time = max(0, 8 - int(elapsed_time))
                    cv2.putText(image, f"Time left: {remaining_time}s", (70, 100),
                                cv2.FONT_HERSHEY_SIMPLEX, 1, (255, 0, 0), 2, cv2.LINE_AA)

                    if elapsed_time >= 8:
                        # 完成一次有效重复
                        counter += 1
                        is_timing = False  # 停止计时

        cv2.imshow('Mediapipe Feed', image)

        if cv2.waitKey(10) & 0xFF == ord('q'):
            break

cap.release()
cv2.destroyAllWindows()

关键改动说明

  1. 移除嵌套循环:所有逻辑在主帧循环中完成,保证每帧都能被处理和渲染,彻底解决画面冻结问题。
  2. 状态变量跟踪:新增is_timing标记计时状态,start_timing记录计时起始时间,逐帧计算已流逝时长,而非用循环阻塞等待。
  3. 逻辑优化:
    • 腿部弯曲且未计时时,自动启动计时
    • 计时过程中实时显示剩余时间
    • 完成8秒计时后自动增加重复计数
    • 计时未完成就伸直腿部时,立即提示用户并停止计时

内容的提问来源于stack exchange,提问作者Below_Average

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.09 01:05:26