yuming 028fcbe990 fix: 修复批量下载漏检测稀疏行和底部半行
根因:
1. std 横向采样区 x=350-1050 不覆盖左列卡片(中心x=201),
   导致只有左列有卡片的稀疏行 std 被拉低到27,低于阈值28被误判为间隙
2. 扫描范围 ROW_Y1=2300 太靠上,底部半行被边缘过滤
3. 每轮只处理前3行(full_rows[:3]),后续行即使检测到也被跳过

修复:
- ROW_STD_X0 350→50, ROW_STD_W 700→980(覆盖全部3列)
- ROW_STD_LOW 28→18(分组标签std=0不会误判)
- ROW_Y1 2300→2380(扩大底部扫描范围)
- 处理所有检测到的行,未打勾格子最多取前9个避免超并发
2026-09-18 18:05:33 +08:00

ezviz-sync — 萤石摄像头录像同步与智能分析

从萤石(EZVIZ)摄像头同步 SD 卡报警录像到本地,对录像做 AI 分析(人物检测、摔倒检测、活动语义打标),并提供 Web 界面浏览和回放。

目标设备:萤石 CB60,序列号 BH5455783。

目录结构

ezviz-sync/
├── automate.py              # 主力下载脚本:ADB 自动化模拟器内萤石 App 批量下载
├── sync.py                  # [已废弃] 开放平台 API 下载方案,保留作备选
├── probe.py                 # API 探测调试脚本
├── analyze.py               # 视频分析主入口(7 阶段流水线)
├── config.py                # 萤石开放平台配置(密钥,已 gitignore)
├── config.example.py        # 配置模板
├── requirements.txt         # 直接依赖
├── requirements.lock        # 锁定版本的完整依赖(pip freeze)
├── recordings/              # 下载的录像,按 设备号/日期/ 归档
├── analysis.db              # SQLite 元数据库(运行时生成)
├── thumbnails/              # 事件缩略图(运行时生成)
├── transcoded/              # Web 播放转码缓存(运行时生成)
├── analysis/                # 分析模块
│   ├── db.py                # SQLite 建表与 CRUD
│   ├── config.py            # 分析参数配置(阈值、模型名、路径)
│   ├── video_scanner.py     # 扫描录像文件入库 + MD5 查重
│   ├── motion_filter.py     # MOG2 背景建模检测活动窗口
│   ├── person_tracker.py    # YOLO11 + ByteTrack 人物跟踪
│   ├── fall_detector.py     # MediaPipe Pose 摔倒检测
│   ├── activity_tagger.py   # Ollama VLM 活动语义打标
│   ├── thumbnail.py         # 事件缩略图抽取
│   ├── timestamp_reader.py  # OSD 时间戳水印识别(模板匹配)
│   ├── backfill_start_time.py  # 精确时间戳回填
│   ├── dedup_finder.py      # 重复视频检测(生成报告)
│   ├── dedup_cleanup.py     # 重复视频清理(默认 dry-run)
│   ├── cleanup_orphans.py   # 清理文件已删除的数据库孤儿记录
│   ├── hashing.py           # MD5 计算
│   ├── ollama_client.py     # Ollama VLM 调用封装
│   ├── frida_spawn.py       # Frida SSL pinning 绕过(抓包用)
│   ├── unpin.js             # Frida 脱壳脚本
│   ├── models/              # MediaPipe pose 模型权重
│   └── templates/           # OSD 数字识别模板库
├── webapp/                  # Flask Web 界面
│   ├── app.py               # 路由:事件列表、统计、视频流、缩略图
│   ├── transcode_cache.py   # 按需转码缓存(HEVC→H264)
│   ├── templates/index.html
│   └── static/              # app.js, style.css
└── yolo11n.pt               # YOLO11 目标检测权重(首次运行自动下载)

环境依赖

工具 用途 安装
Python 3.12 运行环境 pyenv install 3.12 或系统包管理器
ffmpeg / ffprobe 视频处理、抽帧、转码 brew install ffmpeg
ImageMagick (magick) 截图像素分析(automate.py 蓝勾/行检测) brew install imagemagick
Android SDK + ADB 模拟器控制 brew install --cask android-platform-tools
Android 模拟器 运行萤石 App Android Studio AVD 或第三方模拟器
Ollama 本地 VLM 推理(活动打标) brew install ollama && ollama pull qwen2.5vl:7b

安装

# 1. 克隆项目
git clone <repo> && cd ezviz-sync

# 2. 创建虚拟环境
python3.12 -m venv .venv
source .venv/bin/activate

# 3. 安装依赖(用 lock 文件保证版本一致)
pip install -r requirements.lock

# 4. 配置萤石开放平台
cp config.example.py config.py
# 编辑 config.py 填入 AppKey / AppSecret / 设备序列号 / 验证码

# 5. 初始化数据库(首次运行 analyze.py 自动建表)
python analyze.py --only-stage scan

使用方法

1. 下载录像

# 下载昨天的录像(默认)
python automate.py

# 下载最近 7 天的录像
python automate.py 7

下载流程:启动模拟器内萤石 App → 导航到指定日期 → 批量下载 → adb pull 到本地 recordings/设备号/日期/。

2. 分析录像

# 跑完整流水线(scan → backfill → motion → person → fall → activity → thumbnail)
python analyze.py

# 只跑某个阶段
python analyze.py --only-stage motion

# 小批量测试
python analyze.py --only-stage person --limit 10

# 强制重跑某个阶段(回退已完成的视频状态)
python analyze.py --only-stage activity --force

流水线状态机:pending → motion_done → person_done → fall_done → activity_done,每个阶段只处理上一阶段完成的视频,重复运行幂等。

3. 启动 Web 界面

python -m webapp.app
# 打开 http://127.0.0.1:5050

功能:按事件类型/日期筛选、点击事件跳转播放对应时间点、转码缓存管理。

4. 维护工具

# 检测重复视频(只生成报告,不删除)
python -m analysis.dedup_finder

# 清理重复视频(先预览,确认后加 --execute)
python -m analysis.dedup_cleanup
python -m analysis.dedup_cleanup --execute

# 清理数据库中文件已不存在的孤儿记录
python -m analysis.cleanup_orphans        # dry-run
python -m analysis.cleanup_orphans --execute

分析流水线详解

阶段 输入状态 输出状态 方法 说明
scan — pending/corrupt/duplicate ffprobe + MD5 扫描 recordings/ 入库,按 MD5 标记重复
backfill pending pending(不变) 模板匹配 OCR 读取画面 OSD 时间戳,回填精确 start_time
motion pending motion_done MOG2 背景建模 检测有活动的时间窗口,空场景跳过后续重模型
person motion_done person_done YOLO11n + ByteTrack 只在活动窗口内跑人物检测跟踪
fall person_done fall_done MediaPipe Pose 只在有人的时间段跑姿态估计,规则引擎判摔倒
activity fall_done activity_done Ollama Qwen2.5-VL 活动窗口抽帧,VLM 分类为 走动/进食/坐下/站立/离开/其他
thumbnail — — ffmpeg 抽帧 给每条事件生成缩略图

关键设计决策

  • 下载用 ADB 自动化而非开放平台 API:开放平台 API 的 FLV 回放流把 24 小时录像拼接成一个文件,丢失了分段信息和精确时间戳;ADB 方案直接下载 App 内的分段报警录像,文件名含时间戳。
  • 分析流水线分层过滤:先用轻量 MOG2 预筛活动窗口,再在窗口内跑 YOLO,有人的时间段才跑姿态估计和 VLM,避免对空场景浪费算力。
  • 摔倒检测用规则引擎而非 VLM:安全信号需要可解释、可调阈值、宁可多报不漏报,MediaPipe 关键点 + "直立骤变横躺并持续"规则比模型黑盒更可靠。
  • Web 播放按需转码:源文件是 HEVC 编码且 mp4 容器 sample entry 标签缺失,浏览器无法直接播放;第一次播放时用 VideoToolbox 硬件转码为 H264 并缓存,之后复用。

常见问题

Q: automate.py 报错 magick: command not found A: 没装 ImageMagick。brew install imagemagick。automate.py 的蓝勾检测和卡片行定位都依赖 magick 命令。

Q: analyze.py 跑 activity 阶段很慢/卡住 A: 检查 Ollama 是否在运行:ollama ps。模型 qwen2.5vl:7b 需要约 6GB 显存/内存。

Q: Web 端视频播放不了 A: 首次播放会触发转码(HEVC→H264),大文件可能需要几十秒。转码缓存在 transcoded/,可在 Web 界面清空。

Q: 数据库里有很多 duplicate 记录 A: automate.py 早期版本有重复下载问题(已修复),同一段录像被下载成多份。scan 阶段按 MD5 自动标记 duplicate 并跳过分析。可用 dedup_cleanup.py 清理多余文件。

S
Description
No description provided
Readme 459 KiB
Languages
Python 95.3%
JavaScript 3.4%
CSS 0.7%
HTML 0.6%