Files
FishServer/fish_api
2026-05-14 17:17:10 +08:00
..
2026-05-14 17:17:10 +08:00
2026-05-14 16:49:05 +08:00
2026-05-06 15:59:38 +08:00

Recalculate Everything:

CLEAR_SQLITE_DATABASE=1 CLEAR_MEASURE_OUTPUT=1 CLEAR_ACTION_OUTPUT=1 CLEAR_MEDIA=1 CLEAR_STREAM_TMP=1 LD_PRELOAD=/lib/aarch64-linux-gnu/libGLdispatch.so.0 bash scripts/start_fresh.sh

CLEAR_SQLITE_DATABASE=1 CLEAR_MEASURE_OUTPUT=1 CLEAR_ACTION_OUTPUT=1 CLEAR_MEDIA=1 CLEAR_STREAM_TMP=1 bash scripts/start_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
MEASURE_FINAL_AGGREGATE_MODE 齐套后对各段 former 体重/体长聚合:median / mean / trimmed_mean median
可在 fish_api/.env 中填写上述变量(pydantic-settings 会读取)。

安装与启动

cd fish_api
python3 -m pip install -e .   # 或python3 -m pip install -r requirements.txt
bash start_fresh.sh             # 默认仅重置 client_id 投递进度,保留 SQLite 历史与快照
# CLEAR_SQLITE_DATABASE=1 bash start_fresh.sh  # 需要时才彻底清 SQLite
# python3 -m uvicorn app.main:app --host 0.0.0.0 --port 8000  # 需自行 prestart

SVO 输入与 measure_watch(两种来源,同一套目录逻辑)

后台 **[measure_watch](app/services/measure_watch.py)** 只认 **MEASURE_WATCH_DIR** 下一层的 **fish{N}/** 子目录及其中的 **.svo2**(见 iter_svo2_folders)。与入口无关:

方式 说明
ZED 分段录制 每次会话分配 fish_id:取库表(fish_idoutput_dir 路径)、父目录下 fish+数字 子目录名、以及 fish{N}/ 下已有 .svo2 路径 四者编号的最大值再加 1磁盘有数据而库未记时也不冲突文件写入 {MEASURE_WATCH_DIR}/fish{N}/(若未配置 MEASURE_WATCH_DIR{STREAM_TMP_DIR}/zed_svo2/fish{N}/,此时不会启用 measure_watch,除非把 MEASURE_WATCH_DIR 指到 …/ingest/zed_svo2
手工拷贝 .svo2 放入 MEASURE_WATCH_DIR/fish{N}/(自建 fish{N} 即可)

逐段测量与齐套 final:对每个 .svo2 稳定后轮询跑 FishMeasure写入 SQLite服务端可在 calculation_log 中区分 segment/final。GET /api/v1/biomass/real/camera/data.result[] 仅含 idtypelengthweightdate(与历史客户端约定一致);按投递顺序先可能收到多段再收到聚合行。video_left / video_right 规则不变。

ingest/api/v1/ingest/svo/... 为分块上传流程,落盘与上述 fish{N} 路径独立;fish{N} 目录的测量以 measure_watch 为准。

ZED 相机fish_api 启动/停止不会自动开关相机录制;录制由独立服务或仓库根 start_recording.sh / zed_record_cli 等自行管理,写入 MEASURE_WATCH_DIR/fish{N}/ 后由 measure_watch 消费。需要时也可调 /api/v1/zed/recording/start|stop(不经过 uvicorn lifespan

ZED 分段录制 CLI

每次 开始/停止 录制会在 SQLite 表 zed_recording_sessions 中记录一行(fish_idstarted_at / stopped_atoutput_dir;分配规则见上表)。

fish_api 目录下执行(与 app 包路径一致;依赖已 pip install -e .

# 本机直连相机前台录制终端不关Ctrl+C 结束(终端会打印分配的 fish_id
python3 -m app.zed_record_cli start [--segment-sec SEC]

# 仅向已运行的 fish_api 发请求(不阻塞)
python3 -m app.zed_record_cli start --remote [--segment-sec SEC]

# 停止 / 查询状态HTTP需 uvicorn 已监听;基址见 FISH_API_BASE_URL 或 PUBLIC_BASE_URL
python3 -m app.zed_record_cli stop
python3 -m app.zed_record_cli status

若已通过 pip install -e . 安装了控制台脚本,也可使用 fish-zed-record(与上表等价)。

也可使用仓库根目录下的封装脚本:

# 在 FishServer 仓库根目录执行
./start_recording.sh              # 本地前台;可选 --remote / --segment-sec
./stop_recording.sh               # HTTP 停止(需 API 已启动)

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 帧,对选中帧的预测重量取算术平均(与日志中 top{k}_avg 一致),pred_weight_rule = "top_k_aggregate"

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

Debug单条鱼测量与 fish_api 同逻辑)

不启动 uvicorn、不写 SQLite不发布 MEDIA_ROOT(与 run_full_measure_batch 相比仅少快照与媒体发布FishMeasure 子进程与 measure_output/fish{N} 与线上一致)。

调用 [app/services/measure.py](app/services/measure.py) 中的 run_measure_batch_subprocess,配置与 fish_api/.env 相同(get_settings())。

必须在 fish_api 下执行 python -m app...,或从仓库根用下面脚本;若在仓库根直接运行 python -m app.measure_debug_cli,会因找不到 app 包报错(ModuleNotFoundError: No module named 'app')。

# 方式 A仓库根推荐
bash scripts/measure_debug.sh --fish-id 14

# 方式 B先进入 fish_api
cd fish_api
python3 -m pip install -e .   # 若尚未安装依赖
# 默认MEASURE_WATCH_DIR/fish{N}/ 下所有 .svo2 → 输出到 MEASURE_OUTPUT_ROOT/fish{N}(默认 fish_api/.data/measure_output/fish{N}
python3 -m app.measure_debug_cli --fish-id 1

# 或等价入口(须在 fish_api 目录,且已 pip install -e .
fish-measure-debug --fish-id 1

# 指定 SVO 目录或输出目录(在 fish_api 目录下)
python3 -m app.measure_debug_cli --batch-folder /path/to/fish1 --fish-id 1 --output-root /path/to/out

结束后会在终端打印 weight_prediction.json 中的 pred_weight_gpred_weight_rule 等摘要。

演进建议

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