cli to control zed camera start and stop. 2. measure now use every svo2 file for 1 fish, give intermideate result and final result with confidecne level(*).

This commit is contained in:
kevin
2026-04-16 11:38:30 +08:00
parent 9dce487c79
commit cc6cef0f73
57 changed files with 1877 additions and 386 deletions

View File

@@ -56,6 +56,11 @@ class Settings(BaseSettings):
default="http://127.0.0.1:8000",
validation_alias=AliasChoices("PUBLIC_BASE_URL", "public_base_url"),
)
#: ZED 录制 CLI``fish-zed-record``)等访问本机 API 时的基址;未设时与 ``public_base_url`` 相同。**FISH_API_BASE_URL**
fish_api_base_url: str = Field(
default="",
validation_alias=AliasChoices("FISH_API_BASE_URL", "fish_api_base_url"),
)
ingest_api_key: str = ""
@@ -84,6 +89,9 @@ class Settings(BaseSettings):
python_fish_measure: str = ""
python_fish_action: str = ""
#: ffmpeg 可执行文件路径;为空时按顺序尝试 tools/ffmpeg/bin/ffmpeg → 系统 PATH。**FFMPEG_PATH**
ffmpeg_path: str = ""
#: SAM/CUDA 设备cuda 或 cpu
sam_device: str = "cuda"
@@ -261,15 +269,57 @@ class Settings(BaseSettings):
measure_watch_recursive: bool = False
#: 状态管理true=持久化到 SQLite重启后记住false=内存模式(重启后清空)
measure_watch_use_state_file: bool = True
#: 齐套后对各段 former 体重/体长聚合方式:``median``、``mean``、``trimmed_mean``(至少 3 段时去头尾再均值)。**MEASURE_FINAL_AGGREGATE_MODE**
measure_final_aggregate_mode: str = Field(
default="median",
validation_alias=AliasChoices(
"MEASURE_FINAL_AGGREGATE_MODE", "measure_final_aggregate_mode"
),
)
#: 分段 SVO2 输出目录;未设时:有 MEASURE_WATCH_DIR 则为 ``{MEASURE_WATCH_DIR}/fish{N}``N 见 zed_svo_record_fish_id否则为 ``{STREAM_TMP_DIR}/zed_svo2``。**ZED_SVO_RECORD_DIR**
zed_svo_record_dir: Optional[Path] = Field(
default=None,
validation_alias=AliasChoices("ZED_SVO_RECORD_DIR", "zed_svo_record_dir"),
)
#: 每段时长(秒),默认 3005 分钟)。**ZED_SVO_SEGMENT_SEC**
zed_svo_segment_sec: float = Field(
default=300.0,
ge=1.0,
validation_alias=AliasChoices("ZED_SVO_SEGMENT_SEC", "zed_svo_segment_sec"),
)
#: 仅连接一台相机时可选;指定序列号打开对应设备。**ZED_SERIAL_NUMBER**
zed_serial_number: Optional[int] = Field(
default=None,
validation_alias=AliasChoices("ZED_SERIAL_NUMBER", "zed_serial_number"),
)
#: 与 MEASURE_WATCH_DIR 组合为 ``fish{N}``(默认 1。**ZED_SVO_RECORD_FISH_ID**
zed_svo_record_fish_id: int = Field(
default=1,
ge=1,
validation_alias=AliasChoices(
"ZED_SVO_RECORD_FISH_ID", "zed_svo_record_fish_id"
),
)
default_fish_species: str = "大黄鱼"
@field_validator("zed_serial_number", mode="before")
@classmethod
def _zed_serial_empty_none(cls, v: object) -> object:
if v is None:
return None
if isinstance(v, str) and not v.strip():
return None
return v
@field_validator(
"action_watch_dir",
"biomass_water_video_source",
"biomass_sonar_video_source",
"biomass_sonar_video_dir",
"measure_watch_dir",
"zed_svo_record_dir",
mode="before",
)
@classmethod
@@ -300,6 +350,25 @@ class Settings(BaseSettings):
/ "checkpoints"
/ "best_model.pth",
)
if self.zed_svo_record_dir is None:
if self.measure_watch_dir is not None:
object.__setattr__(
self,
"zed_svo_record_dir",
(
self.measure_watch_dir / f"fish{self.zed_svo_record_fish_id}"
).resolve(),
)
else:
object.__setattr__(
self,
"zed_svo_record_dir",
(self.stream_tmp_dir / "zed_svo2").resolve(),
)
else:
object.__setattr__(
self, "zed_svo_record_dir", self.zed_svo_record_dir.expanduser().resolve()
)
return self