Sign inSign up

tingfe/person-timelapse-nas

By tingfe

•Updated 4 days ago

Local person timelapses for NAS. ARM64, Web UI, resumable tasks and multi-day videos.

Image
0

3.4K

tingfe/person-timelapse-nas repository overview

⁠人物延时摄影 NAS

从小米摄像头等设备导出的 MP4 中筛选含有人物的时段,并生成延时摄影视频。适合将大量家庭监控录像留在 NAS 上本地处理。

原始录像始终以只读方式挂载:工具不会移动、改名或删除任何源文件。人物识别、缩略图、事件索引和成片都只写入独立的输出目录。

⁠功能

  • 自动识别常见小米录像文件名及无摄像头编号的历史录像;
  • 人物检测、事件缩略图、可播放的延时成片;
  • 日期索引在后台建立,适合大量历史 MP4;
  • 年度总览与月历,可直接选择整月、全年或自定义日期范围;
  • 单并发任务队列,降低低配置 NAS 的负载;
  • 支持将多个扫描任务依次排队;
  • 支持一键选择起止日期:自动扫描尚未完成的录像,再合成一条多日延时视频;
  • 范围任务独立显示日期处理总进度、合成阶段与成片入口;
  • 成片库支持年份、月份、摄像头、文件名筛选及分页,跨月/跨年视频按覆盖范围匹配;
  • 每个源文件完成后立即保存检查点,NAS 与 Mac 的结果不会相互覆盖;
  • 已处理录像自动跳过;提高扫描精度时只补扫不满足新设置的文件;
  • 内置密码访问、实时进度、暂停/继续队列、取消及失败重试。

⁠快速开始

镜像已公开发布至 Docker Hub:tingfe/person-timelapse-nas⁠。推荐使用 Compose 部署:

当前发布的镜像仅支持 Linux ARM64,适用于极空间 Z2 Pro 等 ARM64 设备;Intel/AMD 的 x86 NAS 暂不能直接使用此镜像。

services:
  person-timelapse:
    image: tingfe/person-timelapse-nas:latest
    container_name: person-timelapse
    environment:
      # 使用强密码;也可留空,首次启动后在 output/.access-password 查看随机密码
      - AUTH_PASSWORD=${AUTH_PASSWORD:-}
    volumes:
      # 改为你的录像目录;务必保留 :ro
      - "/path/to/your/camera-recordings:/input:ro"
      # Compose 项目目录下自动持久化结果和模型
      - "./output:/output"
      - "./models:/models"
    ports:
      - "8790:8790"
    restart: unless-stopped

启动后在同一局域网打开:

http://NAS 的局域网 IP:8790

首次访问需要输入密码。推荐在极空间项目的环境变量中设置 AUTH_PASSWORD;若留空,服务会生成随机密码,并保存到持久化的 output/.access-password(也会打印在首次启动日志中)。

首次启动会后台读取录像目录并建立日期索引,索引元数据持久化为 output/inventory.sqlite3。有旧索引时会先显示旧索引,并在后台检查变化;没有索引时需等待首次索引发现录像日期后再创建任务。默认每小时检查录像目录,管理页的“检查新增录像 / 刷新索引”可立即触发检查。页面刷新和任务状态查询不会反复遍历整个录像目录。

⁠极空间部署

在 Docker 的“创建项目 / Compose 配置”中粘贴上面的配置。录像目录请使用“查询路径”获得的真实容器宿主机路径,例如:

- "/tmp/zfsv3/磁盘标识/data/摄像头文件备份:/input:ro"

不要加入旧版本示例中的 working_dir 或 command。更新时点击“重新构建”;也可以固定版本以避免自动更新:

image: tingfe/person-timelapse-nas:sha-提交哈希

⁠使用方式

  1. 等待录像日历出现。先选年份查看全年总览,点击月份进入月历;状态标记区分待扫描、部分完成、已扫描、执行中与有成片。
  2. 点击“选择整月”或“选择全年”快速填写范围,也可手动填写日期。点击“设为开始 / 设为结束”后,再点击日历中的录像日,可以分别设置范围两端;这一步只填写范围,不会自动启动计算。
  3. 确认摄像头与性能档位,点击“一键人物延时”。系统会跳过无录像日期,补扫范围内尚未完成的素材,再自动合成一条范围视频。若只想扫描或只想使用已有事件合成,使用“只扫描人物”或“只制作延时”。
  4. 在“范围任务”分别查看“人物扫描”和“视频合成”两条进度,完成后直接点击“观看成片”。扫描 100% 不代表合成接近结束。页首显示当前文件、已输出帧数、片段复用数、处理速度、预计成片时长和预计剩余时间;估算会随素材难度变化,刚启动时显示“计算中”。
  5. 在“延时成片库”按年份、月份、摄像头或文件名筛选视频;跨年成片在覆盖的每个年份均可找到。视频在独立播放窗口中观看,后台状态刷新不会重新加载播放器。人物事件、队列和成片均分页显示,避免无限长列表。

范围计划保存在原有的 output/tasks.json 中,旧的导出任务会根据已有日期和依赖记录自动归组,无需重新扫描。完成的范围任务只展示最近 12 项,所有有效成片仍可通过成片库筛选和翻页查找。自动刷新会保留正在选择的年份、月份、范围、筛选条件与页码。

任务队列始终只同时执行一个扫描或导出任务,运行中仍可追加新任务。点击“暂停队列”会停止当前任务并保留扫描检查点和已经校验的合成片段。NAS 或容器重启后,原任务会自动恢复排队,范围合成的前置依赖也会保留。

合成使用临时文件,校验通过才替换原成片;失败不会覆盖已有视频。坏文件会记录为失败而不是“已处理”。若只有部分素材可读,任务标记为“部分完成”,请查看日志后使用“重试 / 继续”。历史录像会先读取真实视频时长,将超出实际结尾的事件截断,并单独显示“时长校正”,不把这种偏差误报为解码损坏。

延时合成默认使用 关键帧快速抽样:每段事件批量抽帧,先缩小画面,再进行合成,避免为每张图片反复解码整段高分辨率 H.265。画面来自相邻关键帧,可能重复或有时间偏差,适合家庭回顾,不适合逐秒精准取证。需要精确解码时,在容器环境变量中设置 EXPORT_DECODE_MODE=accurate(速度会明显降低)。成片旁的 JSON 记录实际使用的 decode_mode。

单批最多抽取 30 帧,解码过程中持续更新心跳;超过 120 秒未完成的批次会停止并记录失败,其他素材继续处理。可通过 EXPORT_DECODE_TIMEOUT_SECONDS 调整此上限。原始录像不会被转码或修改。

延时合成也支持断点续做:每个源录像生成一个经过校验的 1280×720 片段(保持原比例,必要时补黑边),保存在 output/.export-cache/v1/。暂停或重启后只重做未完成的源录像;制作更大日期范围时,相同素材、事件和参数的片段也可复用,最后直接拼接,不重复编码。源文件大小/修改时间、事件、抽帧间隔、帧率或解码模式变化时会自动重做对应片段。读取不完整的片段不会被当作完整检查点复用。

片段缓存会额外占用磁盘空间,但不包含原录像副本。请保持 /output 持久化,运行任务时不要删除或移动缓存;备份整个 output 可同时保留队列、索引、扫描结果和合成检查点。旧版本运行中的合成没有片段检查点,升级无法恢复其尚未保存的中间帧;建议等旧任务结束后再更新。

⁠更新与恢复
  1. 更新前先停止项目,备份整个 output 目录;录像源目录不需要移动。
  2. 拉取新镜像并重新创建容器,保留原来的 /output、/models 挂载位置和访问密码。仅重启旧容器不会更新镜像。
  3. 旧版 processed.json 与事件文件会继续读取,新扫描检查点保存在 output/scan-results/日期/,旧事件备份在 output/legacy-events/。不要只迁移 processed.json,也不要删除 scan-results。
  4. 正在运行的任务会自动续排;失败或阻塞的任务可点击“重试 / 继续”。提高档位会自动补扫,也可勾选“按所选档位重新扫描”明确重做。

早期版本留下的任务会自动补齐进度文件、日期列表和扫描档位;没有记录档位的旧扫描使用“极速”。迁移前的完整队列保存在 output/tasks.pre-migration-*.json,原任务编号、范围合成依赖和已完成状态保持不变。任务配置或启动失败会明确显示错误,并允许后续任务继续,不再停留在无进度的“运行中”。

默认连续 300 秒没有进度会停止卡住的任务。扫描会记录该文件失败,并尝试继续剩余文件;连续超时次数有上限,避免无休止重启。确实需要更长单文件处理时间时,可在 Compose 的 environment 中加入 TASK_STALL_SECONDS=600。索引检查间隔可通过 INDEX_REFRESH_SECONDS 调整(单位:秒,默认 3600)。

旧镜像不认识新的文件级检查点;如需回退,请使用更新前的整份输出目录备份,避免重复扫描或状态不一致。不要用删除输出目录的方式排障。

⁠镜像下载与更新速度

镜像将 AI / FFmpeg 运行环境与程序代码分层保存。基础镜像固定到内容摘要,发布时从 Docker Hub 的上一版镜像恢复构建缓存;只修改程序代码时,NAS 可复用已下载的依赖层,只下载变化的代码层。首次安装、首次启用缓存、清理过本地镜像或升级依赖后,仍可能需要下载较大的依赖层。

拉取时 Already exists 表示该层已缓存,Downloading 是网络下载,Extracting 是本地解压。下载速度仍取决于 NAS 到 Docker Hub 的网络;不要为了加速而删除现有镜像缓存。浏览器或 Mac 上的代理不会自动应用到 NAS 的 Docker 服务,应在 NAS 支持的设置中单独配置可信代理或镜像源。

⁠性能建议

人物识别完全依赖 CPU 时,长时间监控录像的处理会很慢。建议先从“超极速(历史回放)”开始:120 秒抽帧、256px 推理,适合粗筛大量历史录像;只对需要复查的日期使用“极速”“平衡”或“精细”。

档位适合场景取舍
超极速大量历史回放速度最快,可能漏掉短暂出现的人物
极速日常全天回顾Z2 等低配置 NAS 的折中选择
平衡 / 精细短录像复查更容易捕捉短事件,但耗时明显增加

如果超极速仍无法接受,最有效的方案是让 NAS 仅保存录像,在带 GPU 的电脑上挂载 NAS 目录执行识别。

⁠Mac 间歇计算节点(实验性)

Apple Silicon Mac 可用 Metal/MPS 参与计算。将 NAS 的录像目录与 延时摄影 输出目录通过 SMB 挂载到 Mac 后,在本仓库运行:

export INPUT_ROOT="/Volumes/camera-recordings"
export OUTPUT_ROOT="$INPUT_ROOT/延时摄影"
chmod +x mac-worker.sh
./mac-worker.sh 20000101 5

也可以启动仅限本机访问的可视化控制台:

sh mac-console.sh

/Volumes/camera-recordings 是通用示例,请先将 INPUT_ROOT 改为自己实际的 SMB 挂载目录;OUTPUT_ROOT 必须与 NAS 容器的 /output 指向同一目录。在同一个终端启动控制台和索引工具,以便继承这些环境变量。示例日期与文件名均为虚构,请换成自己的录像日期。

随后打开 http://127.0.0.1:8791,选择日期和本次最多处理的文件数,一键启动或停止;日志会直接显示在页面内。

第二个参数是本次最多处理的未处理 MP4 数量(例如 5),可随时按 Ctrl-C 停止;已完成文件会立即记录在 NAS 的 scan-results,下次运行自动续接。首次运行只会在仓库目录创建 .mac-worker-venv 独立虚拟环境,进度写入本地 .mac-worker-state,不修改系统 Python、Homebrew 或 Docker。NAS 与 Mac 应使用同一版本代码和相同的输出目录,文件锁会避免同时扫描同一文件。

Mac worker 默认会取得“计算优先权”:运行期间 NAS 不会启动新的扫描任务;Mac 正常结束、停止或离线超过 15 分钟后,NAS 队列自动恢复。已在 NAS 运行中的任务不会被强行中断。

Mac 也可以协助建立 NAS 日期索引,不需要安装 AI 依赖:

sh mac-index.sh

它会将共享的 inventory.sqlite3 写入 NAS 输出目录。索引锁会协调 NAS 与 Mac,已有节点建立索引时另一节点会提示稍后重试,不会并发改写数据库。

⁠支持的文件名

文件名时间用于归档,不依赖 NAS 的修改时间。支持例如:

00_20000101090000_20000101091000.mp4
video_0000_10_10_20000102090000_20000102093000.mp4
20000103090000_20000103090030.mp4

前两类中的 00、10 会作为不同摄像头;第三类历史文件会归入“历史录像”摄像头。

⁠命令行(可选)

Web 管理页已覆盖常用操作。如需在容器终端运行:

python /opt/person-timelapse/person_timelapse.py scan /input /output --date 20000101
python /opt/person-timelapse/person_timelapse.py export /input /output /output/events-20000101.json --camera 0

多日导出可连续传入多个事件文件:

python /opt/person-timelapse/person_timelapse.py export /input /output \
  /output/events-20000101.json /output/events-20000102.json --camera 0

⁠隐私与安全

  • 不要将 8790 映射到公网;请仅在家庭局域网使用。
  • 原始录像、缩略图、事件索引、任务状态和模型均不应提交到 Git。
  • 发布前请阅读 开源发布前检查清单⁠。

⁠开发与发布

推送到 main 后,GitHub Actions 会先运行可靠性回归测试,通过后才构建 ARM64 镜像并发布 latest 与 sha-提交哈希 标签。维护者需要在 GitHub Actions Secrets 中配置 DOCKERHUB_USERNAME 与 DOCKERHUB_TOKEN;使用者不需要任何 Docker Hub 凭据。

Docker Hub 的短介绍与 Overview 由独立的 Sync Docker Hub overview 流程维护:README 修改后自动同步,也可在 Actions 中手动运行。它复用上述 Secrets,不重建镜像;发布后读回内容校验,失败会明确报错。Docker Hub 上的文档链接会自动指向 GitHub,不依赖相对路径。出于隐私考虑,镜像不附带构建来源证明(provenance: false),构建记录附件和构建摘要也不公开;仍可通过 sha-提交哈希 标签对应源码版本。流水线会屏蔽事件中的邮箱,并在每次发布后自动检查所有公开镜像标签的配置、元数据和应用代码层。不要在流水线中打印完整环境变量、事件或凭据。

维护者也可手动运行 Audit public image privacy,只读检查公开镜像所有标签的配置、来源证明及应用代码层。检查结果不打印命中的邮箱或凭据;它不下载或审查第三方运行时大层,也不能代替完整的人工安全审计。

发布使用镜像内嵌缓存,不需要额外的缓存服务或缓存标签;请勿删除唯一的 latest 镜像缓存来源。需要刷新系统依赖时,在 Actions 手动运行发布流程并勾选 refresh_dependencies,该次会重新安装依赖,NAS 更新时也可能需要重新下载依赖层。升级 AI 基础环境时需审查并更新 Dockerfile 中的版本和摘要。构建上下文仅包含应用目录和构建说明,不上传录像、输出目录、模型、Mac 虚拟环境或根目录的凭据文件。缓存方案参考 Docker 官方说明⁠。

本地测试:python3 -m unittest discover -s tests -v。真实编解码集成测试需要现有的 OpenCV 和 FFmpeg 环境,并设置 RUN_MEDIA_INTEGRATION=1;测试只使用生成的色块画面,不读取私人录像。

可选界面回归:使用已有的 Playwright 环境,在一个终端运行 python3 -m http.server 18879 --bind 127.0.0.1 --directory app,另一个终端运行 node tests/browser_console.cjs。该脚本只使用模拟数据,检查月历、跨年筛选、分页、刷新保持状态及手机布局,不连接 NAS、不启动实际计算。

Tag summary

Content type

Image

Digest

sha256:59d7d71c9…

Size

1.1 GB

Last updated

4 days ago

docker pull tingfe/person-timelapse-nas