NPU 人形检测给监控瘦身:502GB 一晚压成 235GB,1677 目录零错误

家里监控攒了几百 GB 的录像,大部分是没人时的静止画面。要的是'有人就保留、无人就剪掉',还得快、还得全自动。我用 YOLOv8n(OpenVINO,跑在核显+NPU 混合池)做逐秒人形检测,给 D 盘 1677 个目录 / 7.9 万个文件做了一次全自动瘦身:502.5GB → 235.7GB(压缩 53%),一夜 6.5 小时跑完,0 错误。这篇是完整作战记录。

环境:Core Ultra 5 235H / Arc 140T 核显(copy 切割)/ Intel AI Boost NPU(检测)双池 / 混合 GPU4+NPU3 / OpenVINO 2026.5.0.dev / yolov8n_openvino_model FP16 / ffmpeg 9.0.1 / Windows 计划任务。2026-09-12~13 实测。

为什么从 freezedetect 叛逃到 YOLO

一开始用的是 ffmpeg freezedetect/MOG2/帧差这类'运动检测',但在监控夜景面前全军覆没:噪点抖动会让静止画面被判成'活动',而真正有人走动时帧差又稀碎。它区分不了'静止'和'有人'——这是两个完全不同的问题。用户最终拍板:检测标准 = 有人就保留。这种语义判断只有目标检测模型做得了。

方案夜景问题
freezedetect噪点=活动,误报成串
MOG2 / 帧差有人走动帧差不连续
YOLOv8n 人形检测语义判断,唯一权威

流水线三段式

  1. 检测(GPU4+NPU3 常驻服务):每 3 秒抽 1 帧推 YOLOv8n,产出全局活动段 JSON(含 people_secs / 段区间)
  2. 切割(copy 流复制):按段映射回文件内秒,ffmpeg -ss/-t 每段独立切片,-c:v copy 零质量损失
  3. 合并(concat):demuxer 无缝拼接成片,输出 监控成片\目录名.mp4

从 15x 到 127x:每一步的提速账

这条流水线是三轮迭代磨出来的,每轮都砍掉至少一半时间:

版本端到端x 实时关键改动
v4 NPU 单池~235s15xNPU jobs=6 检测 + 独立切片重编码
v5 GPU+NPU 混合36.2s99x双池并行 + copy 切割替代重编码
v6 640x360 + ratio 0.6~21s172xrawvideo 管道 + 缩半分辨率 + 比例分配
v7 3秒采样 + 全盘~8s/目录127x每 3 秒 1 帧推理量减 2/3,全盘批跑

最关键的两次决策:copy 流复制切割代替重编码(44 片 2.8s vs 单命令重编码 87s,快 24 倍),和 把一个小时视频的检测压到 17s 内(混合池 + 3 秒采样)。

切割为什么要 copy 不重编码

方案耗时(42段/1908s活动)成片
copy 切片 + concat jobs=83.6s208.4MB ✅ 最优
copy jobs=12/164.1/4.4s208.4MB(进程启动开销>收益)
filter_complex + qsv 重编码87.2s120.4MB(重编码慢 24 倍,弃)

视频流是 HEVC,源本就是高压缩,重编码只会又慢又损。既然检测已经精确到秒级区间,直接 -c:v copy 把对应包切出来再拼,零质量损失、无可感知体积代价。这也是 127 倍实时能成立的核心。

音画同步与质量抽查

抽检最终成片(HEVC 1080p@20 + aac 16k 单声道):分辨率/帧率/音频流/时长全部正确;音画偏差 64ms(视频 start 0.064s / 音频 0s)可接受;820s 处画面亮度 117,是有效画面不是黑/绿帧。

全盘部署:计划任务 + 断点续跑

规模吓人:1677 目录 / 79027 文件 / 502.5GB,D 盘机械盘只读、不删原文件。直接命令行后台跑必挂(工具环境杀进程),正解是 Windows 计划任务:

:: 计划任务「监控压缩全量」→ launch_all.py(自包含:确保服务运行→调 batch_run)
:: batch_run.py D:\ --jobs 8 --interval 3 --pad 3 --skip-existing --log batch_log.txt
:: --skip-existing:跳过已完成目录,中断后重跑即断点续传(速率 ~6.1 目录/分钟)

踩过的部署雷:计划任务直接调 python 会因中文路径/D:\ 参数序列化污染而 Last Result=1;cmd 包装脚本又被反斜杠引号多重解析搞崩。最终用 launch_all.py 纯 Python 启动器、参数写死在代码里(D:/ 正斜杠),计划任务只调无参脚本——稳定跑完。

结果

  • 1677/1677 目录全部完成,0 错误;成片 1554 个(123 个无人目录无成片)
  • 体积 502.5GB → 235.7GB,压缩 53%
  • 全程 ~6.5 小时(23:51 → 06:26),机器一边跑检测一边还能干别的
  • 输出统一到桌面 监控成片\目录名.mp4,临时切片自动清理
重要提醒:这只是'保留有人画面',不是无损归档——被剪掉的时间段是监控里没有人的部分,适合留存关键证据/省盘场景;如需全量保留请勿使用。检测精度受 conf、采样间隔影响:3 秒采样 + pad 3 秒在实测里活动覆盖最好(详见吞吐天梯)。

My home surveillance had stacked up hundreds of GB, mostly frames of nothing happening. Goal: keep the moments with people, cut everything else — fast, and fully automatic. Using YOLOv8n (OpenVINO, on a hybrid iGPU+NPU pool) for per-second person detection, I auto-compressed 1,677 directories / 79,027 files on D: — 502.5GB → 235.7GB (−53%) in 6.5 hours overnight, zero errors. This is the full battle log.

Env: Core Ultra 5 235H / Arc 140T (copy-cut) / Intel AI Boost NPU (detection) dual pool, GPU×4+NPU×3 / OpenVINO 2026.5.0.dev / yolov8n_openvino_model FP16 / ffmpeg 9.0.1 / Windows Task Scheduler. Tested 2026-09-12~13.

Why we defected from freezedetect to YOLO

We started with ffmpeg freezedetect / MOG2 / frame-diff 'motion detection', which collapsed against surveillance night footage: sensor noise made static frames look 'active', while real walk-bys came out as fragmented frame diffs. It can't tell 'static' from 'has people' — those are different problems entirely. The final ruling landed on keep-if-there-is-a-person, and only an object detector can make that call.

ApproachNight problem
freezedetectnoise reads as motion
MOG2 / 帧差walk-by diffs fragmented
YOLOv8n person detectionsemantic; the single authority

The three-stage pipeline

  1. Detect (resident GPU×4+NPU×3 service): one frame per 3s through YOLOv8n → global activity-segment JSON (people_secs / intervals)
  2. Cut (stream copy): map segments back to in-file seconds, ffmpeg -ss/-t per slice, -c:v copy = zero quality loss
  3. Merge (concat): demuxer joins slices into the final 监控成片\目录名.mp4

From 15× to 127×: the speed-up ledger

This pipeline came from three rounds of iteration, each halving the time:

VersionEnd-to-end× realtimeKey change
v4 NPU only~235s15xNPU×6 detect + re-encode slices
v5 hybrid36.2s99xdual pool + copy-cut replaces re-encode
v6 640x360 + ratio 0.6~21s172xrawvideo pipe + half-res + ratio split
v7 3s sampling + full disk~8s/目录127xevery-3s inference −2/3, full-disk batch

Two decisions mattered most: stream-copy cutting instead of re-encoding (44 slices in 2.8s vs 87s re-encode — 24× faster), and detecting a full hour of video in under 17s (hybrid pool + 3s sampling).

Why cut with copy, not re-encode

ApproachTime (42 segs/1908s)Output
copy-slices + concat, jobs=83.6s208.4MB ✅ best
copy jobs=12/164.1/4.4s208.4MB (startup cost > gain)
filter_complex + qsv re-encode87.2s120.4MB (24× slower, dropped)

The source is HEVC — already highly compressed, so re-encoding is slow and lossy. With detection accurate to the second, just -c:v copy the matching packets and stitch — zero quality loss, negligible size cost. That's the core of 127× realtime.

AV sync and quality spot-checks

Final outputs spot-checked (HEVC 1080p@20 + AAC 16k mono): resolution/framerate/audio/timing all correct; AV offset 64ms (video start 0.064s / audio 0s) acceptable; frame at 820s has brightness 117 — real content, not black/green.

Full-disk deploy: task scheduler + resume

The scale is brutal: 1,677 dirs / 79,027 files / 502.5GB, D: is read-only and nothing is deleted. A raw background command gets killed; the fix is a Windows scheduled task:

:: 计划任务「监控压缩全量」→ launch_all.py(自包含:确保服务运行→调 batch_run)
:: batch_run.py D:\ --jobs 8 --interval 3 --pad 3 --skip-existing --log batch_log.txt
:: --skip-existing:跳过已完成目录,中断后重跑即断点续传(速率 ~6.1 目录/分钟)

Deployment traps: a scheduled task calling python directly failed (Last Result=1) from Chinese paths / D:\ serialization pollution; a cmd wrapper then broke on backslash-quote parsing. The fix was launch_all.py, a pure-Python launcher with args hardcoded (forward-slash D:/), and the task invoking a no-arg script — ran to completion.

Results

  • 1,677/1,677 directories completed, zero errors; 1,554 finals (123 people-less dirs yielded none)
  • Size 502.5GB → 235.7GB, −53%
  • ~6.5 hours total (23:51 → 06:26), and the box stayed usable throughout
  • All outputs land in Desktop 监控成片\目录名.mp4; temp slices auto-clean
Heads-up:This keeps only people present — not a lossless archive; the dropped intervals are the empty-times of the footage, ideal for evidence-grab / disk-saving, not for full retention. Detection accuracy follows conf and sampling: 3s interval + 3s pad had the best activity coverage in tests (see throughput ladder).
返回文章列表