Files
FishServer/fish_api/README.md
2026-04-14 22:05:52 +08:00

6.2 KiB
Raw Blame History

Fish API

FastAPI 网关:分块接收 SVO2FishMeasureMP4FishAction在后台调用现有脚本对外提供文档约定的 GET 轮询接口,并托管预览视频 URL。

依赖环境

  • Python 3.11+uv 可管理 3.13 等版本)
  • FishMeasure:需与本机 FishMeasure/ 一致的环境ZED SDK、pyzed、CUDA、YOLO/SAM 权重等)
  • FishAction:需可运行 FishAction/predict_video_x3d_3class.pyPyTorch、PyTorchVideo、checkpoint

若 FishMeasure 与 FishAction 使用不同虚拟环境,可设置:

  • PYTHON_FISH_MEASURE — 运行 predict_weigth_from_svo2.py 的解释器路径
  • PYTHON_FISH_ACTION — 运行 predict_video_x3d_3class.py 的解释器路径

单一 Conda 环境:若 FishMeasure 与 FishAction 已与网关停在同一个 env例如仓库根目录 packaging/conda-fishserver.yaml 定义的 fishserver),则不要设置上述两个变量,子进程会使用当前 uvicorn 的 Python。可用 scripts/start_fresh.sh(清空后启动)或 scripts/start_no_fresh.sh(保留缓存)启动。

配置(环境变量)

变量 说明 默认
PUBLIC_BASE_URL 返回 JSON 中 video_left / video_right 的前缀(勿带末尾 / http://127.0.0.1:8000
INGEST_API_KEY 非空时,/api/v1/ingest/* 需请求头 X-API-Key 空(不校验)
STREAM_TMP_DIR 分块上传临时目录 <repo>/fish_api/.data/ingest
MEDIA_ROOT 对外托管每次测量生成的 *_left.mp4 / *_right.mp4 <repo>/fish_api/.data/media
FISH_MEASURE_ROOT FishMeasure 根目录 自动相对仓库
FISH_ACTION_ROOT FishAction 根目录 自动相对仓库
MEASURE_OUTPUT_ROOT 传给 --save-output 的目录 <repo>/fish_api/.data/measure_output
YOLO_MODEL / WEIGHT_CHECKPOINT / ACTION_CHECKPOINT 模型路径 与仓库内脚本默认一致
SAM_DEVICE cudacpu cuda
可在 fish_api/.env 中填写上述变量(pydantic-settings 会读取)。

安装与启动

cd fish_api
uv sync
# 可选:包含 httpx便于本地用 FastAPI TestClient 做冒烟测试
# uv sync --group dev
bash start_fresh.sh    # 默认仅重置 client_id 投递进度,保留 SQLite 历史与快照
# CLEAR_SQLITE_DATABASE=1 bash start_fresh.sh  # 需要时才彻底清 SQLite
# 或uv run uvicorn app.main:app --host 0.0.0.0 --port 8000需自行 prestart

OpenAPIhttp://127.0.0.1:8000/docs

对外 GET由其它系统轮询

  • GET /api/v1/biomass/real/camera/ — 每次 GET 消费该客户端下一条未投递的称重快照SQLite 按客户端独立游标);无新数据时 data.result 为空,响应头 X-Fish-Biomass-New: 0
  • GET /api/v1/biomass/health/result/ — 同上,行为 / 健康快照队列

客户端区分:请求头 X-Fish-Client-Id: <字符串> 或查询参数 client_id=<字符串>优先头)。未携带时等价于 default,与旧版「全局一条游标」行为一致。不同 client_id 各自从当前队列消费,互不影响。

result 中每条鱼含算法字段:id(跟踪 IDweightglengthmm。不可交付或失败的推理不会进入客户端队列。

流式输入(分块上传)

  1. POST /api/v1/ingest/svo/sessionPOST /api/v1/ingest/mp4/sessionsession_id
  2. PUT /api/v1/ingest/{svo|mp4}/session/{session_id}?offset=0(多次)
    • 追加语义offset 必须等于当前已写入字节数(从 0 开始顺序上传)
  3. POST /api/v1/ingest/{svo|mp4}/session/{session_id}/finalize202 Accepted,后台开始跑对应算法

示例(小文件一次性,$API_KEY 可选):

BASE=http://127.0.0.1:8000
H=()
# H=(-H "X-API-Key: your-secret")

sid=$(curl -sS "${H[@]}" -X POST "$BASE/api/v1/ingest/svo/session" | jq -r .session_id)
curl -sS "${H[@]}" -T sample.svo2 -X PUT "$BASE/api/v1/ingest/svo/session/$sid?offset=0"
curl -sS "${H[@]}" -X POST "$BASE/api/v1/ingest/svo/session/$sid/finalize"
curl -sS "$BASE/api/v1/biomass/real/camera/"

MP4 将 svo 换成 mp4,本地文件换成 clip.mp4,轮询 GET /api/v1/biomass/health/result/

说明curl -T 发送 PUT 时 offset 为 0 且一次性传完整文件适合单块场景;多块时请自行递增 offset

行为与健康映射

  • X3D 输出 feeding / normal / scared → 中文 吃饵 / 正常游行 / 惊吓
  • 健康:scared不健康,其余 → 健康(启发式,可后续换专用模型)

视频 URL

FishMeasure 跑完后在输出目录查找 *preview*.mp4,复制到 MEDIA_ROOT/,文件名为 {UTC时间戳}_{svo_stem}_left.mp4 / _right.mp4(每次测量不覆盖;仅一个预览文件时可能左右 URL 指向同一逻辑源经 SBS 拆分)。确保 PUBLIC_BASE_URL 与前端/文档中的域名端口一致。

Weight Rule (Current)

最终体重 pred_weight_g 由以下规则链决定(按优先级从高到低):

  1. 440g 全池均值保护(规则 Bavg_g_filtered(所有 candidates 均值)> --mean-pool-fallback-max-if-over-g(默认 440gpred_weight_g = max_predicted_weight_g_after_filterpred_weight_rule = "max_after_filter_high_mean_pool_over_g"
  2. 400g mean-all fallback(规则 A--average-all-after-filter 开启时):若全池 mean > --average-all-fallback-max-if-mean-over-g(默认 400gpred_weight_g = max_predicted_weight_g_after_filterpred_weight_rule = "max_after_filter_high_mean_all"
  3. --average-all-after-filter(默认关):全部 candidates 均值作为最终值,pred_weight_rule = "mean_all_filtered"
  4. Top-K 聚合(默认路径):按 --top-by-length(默认开)选 top-K 帧candidates < 5 用 max 否则用 meanpred_weight_rule = "top_k_aggregate"

DGCNN 明细中同时输出 mean_all_pred_g_after_filtersavg_topk_mean_pred_g 等供对比参考。

演进建议

  • RTSPffmpeg 切段写入 MP4 后调用现有 finalize 逻辑
  • 任务状态:finalize 返回 job_id,增加 GET /jobs/{id} 查询进度