# 小阳心健康测量 SDK

[![Python](https://img.shields.io/badge/Python-3.10%E2%80%933.11-blue)](https://www.python.org/)

`xy_health_measurement_sdk` 是小阳心健康测量服务的 Python 客户端 SDK。它从视频帧中检测人脸、校验采集质量、提取生理特征，并通过 WebSocket 将数据流式上报至测量服务，最终以事件回调的方式返回阶段性与完整的健康报告。

- [使用手册](https://measurement.xymind.cn/docs/sdk/python.html)
- [示例程序](https://github.com/xiaoyang-tech/measurement-python-client)

## 安装

```bash
pip install xy_health_measurement_sdk
```

> SDK 依赖随包分发的 C++ 动态库（`libmeasurement.so`）及 MediaPipe 模型，对操作系统及 OpenCV / protobuf 版本有要求，详见[运行环境](#运行环境)。

## 快速开始

订阅事件 → 传入首帧启动测量 → 持续入队视频帧，直至完整报告生成。

```python
import cv2
from xy_health_measurement_sdk import Measurement, Event

config = {
    "measurement_duration": 30000,  # 测量时长（毫秒）
}

with Measurement(app_id="<APP_ID>", sdk_key="<SDK_KEY>", **config) as measurement:
    measurement.subscribe(Event.STARTED, lambda sender, **kw: print("started", kw))
    measurement.subscribe(Event.CHUNK_REPORT_GENERATED, lambda sender, **kw: print("chunk", kw["report"]))
    measurement.subscribe(Event.WHOLE_REPORT_GENERATED, lambda sender, **kw: print("report", kw["report"]))
    measurement.subscribe(Event.CRASHED, lambda sender, **kw: print("error", kw["msg_cn"]))

    cap = cv2.VideoCapture("video.mp4")
    _, first_frame = cap.read()
    measurement.start(first_frame)            # 传入首帧，创建测量

    while True:
        ok, frame = cap.read()
        if not ok:
            break
        timestamp = cap.get(cv2.CAP_PROP_POS_MSEC)
        measurement.enqueue(frame, timestamp)  # 持续入队视频帧

    measurement.join()                         # 等待报告生成
```

完整可运行示例见 [`examples/`](examples/) 目录（`video_file_sample.py` 本地视频文件、`rtsp_sample.py` RTSP 流）。

## 公共 API

| 成员 | 说明 |
| --- | --- |
| `Measurement(app_id, sdk_key, *categories, **config)` | 测量入口。`categories` 为可选的测量类目（默认全部），`config` 见下表。 |
| `Measurement.start(frame)` | 以首帧创建并启动测量。 |
| `Measurement.enqueue(frame, timestamp)` | 入队视频帧，`timestamp` 为毫秒时间戳。 |
| `Measurement.validate(frame)` | 测量前校验单帧采集条件，返回错误/告警列表。 |
| `Measurement.subscribe(event, handler)` / `unsubscribe(event, handler)` | 订阅 / 取消订阅事件，`event` 可为 `Event` 成员或等价字符串。 |
| `Measurement.join()` | 阻塞等待测量完成。 |
| `Measurement.interrupt()` / `stop()` | 中断 / 停止测量。 |
| `Event` | 事件枚举，见[事件](#事件)。 |
| `__version__` | SDK 版本号。 |

`Measurement` 同时支持上下文管理器（`with` 语句），退出时自动停止测量并释放资源。

## 配置项

通过关键字参数传入 `Measurement(...)`：

| 参数 | 类型 | 默认值 | 说明 |
| --- | --- | --- | --- |
| `measurement_duration` | int | 服务端上限 | 测量时长（毫秒），超出 [下限, 上限] 将被钳制。 |
| `min_chunk_timespan` | int | 服务端默认 | 阶段性 chunk 的最小时间跨度（毫秒）。 |
| `auth_url` | str | 生产鉴权地址 | 鉴权服务地址。 |
| `measurement_url` | str | 生产测量地址 | 测量 WebSocket 地址。 |
| `logging_config` | dict | 无 | 透传给 `logging.basicConfig` 的日志配置（`level`/`format`/`filename`/`filemode` 等）。 |

## 事件

使用 `Event` 枚举（或等价字符串）订阅。处理器签名为 `handler(sender, **kwargs)`。

| 事件 | 触发时机 | 回调参数 |
| --- | --- | --- |
| `Event.STARTED` | 服务端创建测量成功 | `measurement_id` |
| `Event.COLLECTED` | 视频帧采集完毕 | 无 |
| `Event.CHUNK_REPORT_GENERATED` | 生成阶段性报告 | `report`（dict） |
| `Event.WHOLE_REPORT_GENERATED` | 生成完整报告 | `report`（dict） |
| `Event.STATE_UPDATED` | 采集状态/告警更新 | `states`（list） |
| `Event.INTERRUPTED` | 测量被中断 | 无 |
| `Event.CRASHED` | 发生错误 | `code` / `level` / `msg` / `msg_cn` 等 |

## 运行环境

自 SDK >= 2.2 起随包分发了 C++ 动态库（`libmeasurement.so`），因此对运行平台有约束：

- 运行平台
  - 架构：**x86_64**
  - 操作系统：**Linux**（动态库依赖 glibc 与 libstdc++，需版本足够新；Ubuntu 22.04+ 为已验证的参考基线，并非硬性限定某一发行版）
  - Python：3.10 – 3.11
- 关键依赖（版本以 [`pyproject.toml`](pyproject.toml) 为准）
  - numpy 1.26.4
  - opencv-python-headless 4.9.0.80
  - mediapipe 0.10.30

> 非 x86_64 或非 Linux 平台仍可 `import` 本包并运行纯 Python 单元测试（动态库为惰性加载），但无法完成依赖 `.so` 的真实测量流程。
