篮球裁判犯规识别系统(四) foul_fn函数 上

这个函数之前说到了非常重要,我们会分两篇来讲完,这里先讲一下工作流程:

完整函数如下:

python 复制代码
def foul_fn(left, right, fps, frame):
    left_sh, left_el, left_wr = left
    right_sh, right_el, right_wr = right

    frames_needed = int(0.5 * fps)

    # 判断左右手是否举起
    left_up = left_wr[1] < left_sh[1]
    right_up = right_wr[1] < right_sh[1]
    single_up = (left_up and not right_up) or (right_up and not left_up)

    # ==============================
    # Phase 0:检测单手举起
    # ==============================
    if state.get("phase", 0) == 0:
        if single_up:
            hand = "left" if left_up else "right"
            if state.get("single_hand") == hand:
                state["single_up_count"] += 1
            else:
                state["single_hand"] = hand
                state["single_up_count"] = 1
        else:
            state["single_up_count"] = 0
            state["single_hand"] = None

        if state["single_up_count"] >= frames_needed:
            state["phase"] = 1
            state["history"] = {"left": [], "right": []}
        return None

    # ==============================
    # Phase 1:记录历史并判定犯规
    # ==============================
    max_history = 12
    swing_amp = 10
    lag_tol = 3

    # 如果左右手高度差太大,清空历史
    if abs(left_wr[1] - right_wr[1]) > 50:
        state["history"]["left"] = []
        state["history"]["right"] = []
        return None

    state["history"]["left"].append(left_wr[1])
    state["history"]["right"].append(right_wr[1])
    if len(state["history"]["left"]) > max_history:
        state["history"]["left"].pop(0)
        state["history"]["right"].pop(0)

    hL = state["history"]["left"]
    hR = state["history"]["right"]

    required_count = int(fps * 0.5)

    if is_block(
            left_wr, left_el, left_sh,
            right_wr, right_el, right_sh,
            state, frames_needed
    ):
        return "阻挡"

    bump_result = is_bump(
        left_sh, left_el, left_wr,
        right_sh, right_el, right_wr,
        frame,
        state,
        frames_needed=3,
        dist_thresh=20,
        elbow_angle_thresh=140,
        hand_close_thresh=60,
        finger_var_thresh=50
    )
    if bump_result is not None:
        return bump_result

    pull_result =  is_pull(right_wr, right_sh, left_wr, frame,state, frames_needed)
    if pull_result is not None:
        return pull_result



    if is_push(
            left_sh, left_el, left_wr,
            right_sh, right_el, right_wr,
            frame, state
    ):
        return "推人"


    if is_infringe_cylinder(hL, hR):
        state["consec_infringe"] += 1
    else:
        state["consec_infringe"] = max(state["consec_infringe"] - 1, 0)

    counters = {
        "侵犯圆柱体": state["consec_infringe"]
    }

    # 找分数最高的
    max_type = max(counters, key=counters.get)
    if counters[max_type] > 0:  # 必须至少有连续帧才算
        return max_type
    return None

foul_fn 是项目中 动作识别阶段的"心脏" ,输入左右手关键点和帧率,输出犯规类型(推人、拉人、阻挡、侵犯圆柱体等)。它用 分阶段状态机 + 历史记录 + 阈值判断 实现稳定判定。


一、输入与初始化

python 复制代码
left_sh, left_el, left_wr = left
right_sh, right_el, right_wr = right

frames_needed = int(0.5 * fps)
  • leftright 分别是左右肩、肘、腕的像素坐标。

  • frames_needed 表示连续帧数阈值(0.5秒),用于防抖动


二、单手举起检测(Phase 0)

python 复制代码
left_up = left_wr[1] < left_sh[1]
right_up = right_wr[1] < right_sh[1]
single_up = (left_up and not right_up) or (right_up and not left_up)
  • 判断手是否抬起(y 越小越高)。

  • single_up 表示单手抬起,这是判定犯规动作的触发条件。

状态机逻辑:

python 复制代码
if state.get("phase", 0) == 0:
    if single_up:
        hand = "left" if left_up else "right"
        if state.get("single_hand") == hand:
            state["single_up_count"] += 1
        else:
            state["single_hand"] = hand
            state["single_up_count"] = 1
    else:
        state["single_up_count"] = 0
        state["single_hand"] = None

    if state["single_up_count"] >= frames_needed:
        state["phase"] = 1
        state["history"] = {"left": [], "right": []}
    return None
  • 连续帧检测避免误判。

  • 当单手举起持续超过阈值 → 进入 Phase 1(开始正式犯规判断)。


三、历史记录与噪声过滤(Phase 1)

复制代码
python 复制代码
max_history = 12
swing_amp = 10
lag_tol = 3
  • 维护左右手腕高度的历史 state["history"],限制最大长度。

  • 如果左右手高度差过大(异常动作) → 清空历史,防止误判。

python 复制代码
state["history"]["left"].append(left_wr[1])
state["history"]["right"].append(right_wr[1])
if len(state["history"]["left"]) > max_history:
    state["history"]["left"].pop(0)
    state["history"]["right"].pop(0)

这种历史窗口策略是典型 时间滤波 + 防抖动 的工程手段。


四、具体犯规类型判定

调用不同的规则函数,按优先级顺序判断:

python 复制代码
if is_block(...):
    return "阻挡"

bump_result = is_bump(...)
if bump_result is not None:
    return bump_result

pull_result =  is_pull(...)
if pull_result is not None:
    return pull_result

if is_push(...):
    return "推人"
  • 阻挡撞人/碰撞拉人推人,按顺序判断。

  • 每个规则函数内部都是几何 + 阈值判断,结合手肘角度、手腕位置、距离、手指展开状态等。


五、连续判定与"侵犯圆柱体"

python 复制代码
if is_infringe_cylinder(hL, hR):
    state["consec_infringe"] += 1
else:
    state["consec_infringe"] = max(state["consec_infringe"] - 1, 0)
  • 连续帧累积,保证短暂误动作不会触发犯规。

  • consec_infringe 数值越大 → 犯规可信度越高。

  • 最后根据计数选分数最高的犯规类型返回。


六、工程亮点

  1. 状态机分阶段:Phase 0 防抖动、Phase 1 历史判定 → 避免短时动作误报。

  2. 历史窗口:保持手腕高度历史,减少抖动干扰。

  3. 优先级判断:推人 / 拉人 / 阻挡有明确顺序。

  4. 连续帧计数:只有连续动作才判定为犯规,增强稳定性。

  5. 规则可扩展 :只需修改 is_push / is_pull 等函数,不影响主流程。

下一章我们讲一下

python 复制代码
if is_block(...):
    return "阻挡"

bump_result = is_bump(...)
if bump_result is not None:
    return bump_result

pull_result =  is_pull(...)
if pull_result is not None:
    return pull_result

if is_push(...):
    return "推人"

这些函数,看看具体的判断是怎么实现的

相关推荐
码兄科技11 小时前
Java AI智能体开发实战:从零构建智能对话系统指南
java·开发语言·人工智能
折哥的程序人生 · 物流技术专研12 小时前
第8篇:六大设计原则在工厂模式中的体现
java·设计模式·设计原则·工厂模式·编程进阶·扩充系列
小明bishe1812 小时前
计算机毕业设计之基于JAVA的植物科普网站
java·spring boot·spring·架构·课程设计
r_oo_ki_e_12 小时前
Java Map 集合学习笔记
java·笔记·学习
SL-staff13 小时前
智慧园区2000+设备统一管理:JVS-IoT如何降低运维成本40%
java·开发语言·物联网·智慧园区·设备管理·运维优化·jvs-iot
咖啡八杯13 小时前
GoF设计模式——模板方法模式
java·后端·spring·设计模式
梦帮科技13 小时前
GRAVIS v4.0:基于Web的极速套利架构设计与实时数据流实现
前端·人工智能·rust·自动化·区块链·智能合约·数字货币
爱笑的源码基地14 小时前
一款全开源的信创云PACS影像云平台解决方案,支持多模态医学影像处理(CT/MR/DR/超声/病理等)
java·源码·国产化·pacs·云影像·区域pacs
爱勇宝14 小时前
办公资料反复修改、补传、交接混乱,我做了个桌面工具来解决这件事
前端·后端·程序员
我命由我1234514 小时前
Android 开发问题:ClickableSpan 的点击事件没有生效
java·java-ee·android studio·android jetpack·android-studio·android runtime