支持本地视频测试
This commit is contained in:
z66
2025-12-23 17:59:18 +08:00
parent ae9d252255
commit 1a135cdda7
9 changed files with 301 additions and 14 deletions
+98
View File
@@ -0,0 +1,98 @@
# -*- coding: utf-8 -*-
from datetime import datetime, date
from pathlib import Path
from statistics import mean
class FishHistoryManager:
"""维护每日鱼类活动档案,并在指定时间生成报告。"""
def __init__(self, report_dir: str = "reports", report_hour: int = 20):
self.report_dir = Path(report_dir)
self.report_dir.mkdir(parents=True, exist_ok=True)
self.report_hour = report_hour
self.records = {} # fish_id -> metrics
self.last_report_date: date | None = None
def update(self, track_stats, ts: datetime):
"""用当前帧的跟踪统计更新档案。"""
for stats in track_stats:
fid = stats["id"]
rec = self.records.setdefault(fid, {
"frames": 0,
"distance": 0.0,
"bottom_frames": 0,
"slow_frames": 0,
"speed_samples": [],
"first_seen": ts,
"last_seen": ts,
})
rec["frames"] += 1
rec["distance"] += float(stats.get("distance_delta", 0) or 0)
if stats.get("is_bottom"):
rec["bottom_frames"] += 1
if stats.get("is_slow"):
rec["slow_frames"] += 1
speed = stats.get("speed")
if speed is not None:
rec["speed_samples"].append(float(speed))
rec["last_seen"] = ts
def _analyze_record(self, fid, rec):
frames = rec.get("frames", 1)
bottom_ratio = rec.get("bottom_frames", 0) / frames
slow_ratio = rec.get("slow_frames", 0) / frames
avg_speed = mean(rec["speed_samples"]) if rec["speed_samples"] else 0
distance = rec.get("distance", 0)
issues = []
if avg_speed < 10 or slow_ratio > 0.5:
issues.append("活动偏低")
if bottom_ratio > 0.4:
issues.append("长时间停留底部")
health = "疑似异常" if issues else "正常"
return {
"fish_id": fid,
"frames": frames,
"distance": distance,
"avg_speed": avg_speed,
"bottom_ratio": bottom_ratio,
"slow_ratio": slow_ratio,
"health": health,
"issues": issues,
"active_period": f"{rec['first_seen'].strftime('%H:%M:%S')} - {rec['last_seen'].strftime('%H:%M:%S')}",
}
def generate_report(self, report_date: date | None = None):
"""生成报告文本并写入文件,返回文件路径。"""
report_date = report_date or date.today()
analyses = [self._analyze_record(fid, rec) for fid, rec in self.records.items()]
analyses.sort(key=lambda x: x["fish_id"])
lines = [f"鱼类日度报告 - {report_date.strftime('%Y-%m-%d')}"]
if not analyses:
lines.append("今日无检测数据。")
else:
for item in analyses:
issues_text = "".join(item["issues"]) if item["issues"] else ""
lines.append(
f"- 鱼 {item['fish_id']}: 状态={item['health']},平均速度={item['avg_speed']:.1f}px/s"
f"底部占比={item['bottom_ratio']:.2f},慢速占比={item['slow_ratio']:.2f}"
f"累计位移={item['distance']:.1f}px,时间段={item['active_period']},问题={issues_text}"
)
report_path = self.report_dir / f"fish_report_{report_date.strftime('%Y%m%d')}.txt"
report_path.write_text("\n".join(lines), encoding="utf-8")
self.last_report_date = report_date
# 每日报告后可选择清空记录,当前继续保留,次日将覆盖分析。
return report_path
def maybe_generate_report(self, now: datetime):
"""在到达指定时间且今日未生成报告时生成报告。"""
if now.hour >= self.report_hour:
today = now.date()
if self.last_report_date != today:
return self.generate_report(today)
return None