简介这份资源面向计算机视觉入门者与智能交通、视频监控方向的开发者提供一套基于Python的行人检测完整实现用HOG特征配合SVM分类器完成视频帧中的行人识别并叠加简单跟踪算法维持连续帧间的目标位置。压缩包共25个文件、约155.2MB包含2个py脚本承载检测主流程与辅助逻辑2个avi视频用于实际检测演示11个bmp与5个png样本图像可作训练或测试素材另有4个xml配置文件及工程描述文件便于直接导入IDE运行调试。已有8175人学习下载说明该案例在同类入门项目中认可度较高。读者可从中获得从HOG特征提取、SVM二分类训练到检测框输出与跟踪校正的完整代码链路结合样本图像与演示视频理解多尺度窗口划分、梯度直方图构造等关键环节并在此基础上迁移到其他物体检测任务适合作为课程设计或项目练手的参考实现。1. 行人检测视频源码包从一段视频里把每个人都框出来手头有一段监控视频想让它自动把画面里的行人一个个框出来这件事听起来像要搭一整套深度学习环境实际上一个 Python 行人检测视频源码包就能跑起来。它做的事情很直接读入视频流逐帧做计算机视觉行人检测把检测到的行人用矩形框标出来再把处理后的帧写回成新视频。适合两类人一类是刚接触计算机视觉、想找个能跑通的完整例子练手的开发者另一类是手里有视频数据、想快速验证检测效果再决定要不要深入做的工程同学。源码包的好处是链路完整从读视频、预处理、推理到写视频全都在不用自己拼装。下面按「能干什么、怎么跑、怎么改」的顺序拆开讲中间会穿插我踩过的坑。2. 项目定位与技术选型为什么是 OpenCV 加检测模型这套组合2.1 这个源码包到底解决什么问题行人检测视频源码包的核心价值是把「视频」和「检测」两件事粘在一起。单张图片的行人检测例子网上很多但视频不一样视频有帧率、有编码格式、有分辨率读进来是一帧一帧的图像数组处理完还要按原来的节奏写出去中间任何一步参数不对出来的视频要么卡顿、要么花屏、要么根本打不开。这个源码包把这条链路打通了你拿到手改改输入路径就能看到框。从功能边界看它一般包含三块视频读取与帧循环、行人检测推理、结果绘制与视频写出。有的版本还会带一个简单的置信度过滤和帧率统计。它不负责训练模型用的是现成的检测权重所以定位是「推理侧的可运行 Demo」不是「从零训练一个行人检测器」。想二次开发的人要清楚这一点改模型结构是另一回事改检测流程和输出才是这个包的主战场。技术栈上最常见的组合是 Python OpenCV 一个轻量检测模型。OpenCV 负责视频 IO 和画框检测模型负责出候选框。为什么不用更重的框架因为视频逐帧推理对速度敏感轻量模型在普通笔记本上也能跑到能看的帧率这是这个包能「开箱即跑」的前提。2.2 技术栈拆解每一层各管什么把技术栈按职责拆开看会清楚很多。第一层是视频 IO 层用 OpenCV 的VideoCapture和VideoWriter前者按帧读后者按帧写中间靠一个 while 循环串起来。第二层是预处理层把 BGR 帧转成模型要的输入格式常见做法是缩放、归一化、转 RGB 或转 blob。第三层是推理层加载检测模型对单帧出框。第四层是后处理层做置信度阈值过滤、非极大值抑制NMS去重最后把框画回原图。这四层里最容易翻车的是第一层和第四层。第一层的坑在编码器和帧率写出来的视频打不开多半是这里第四层的坑在坐标缩放模型输入是缩放后的尺寸框要映射回原图尺寸映射错了框就会偏。下面这张表是我一般会先确认的参数清单环节关键参数常见取值作用视频读取输入路径本地 mp4/avi指定待检测视频视频写出fourcc 编码mp4v / XVID决定输出视频能否播放视频写出fps与原视频一致控制播放速度预处理输入尺寸320/416/640影响速度与精度后处理置信度阈值0.3~0.5过滤低质量框后处理NMS 阈值0.4~0.5去掉重叠框选型上还有一个现实考量检测模型选哪个。轻量级方案在 CPU 上跑视频更现实重模型精度高但帧率掉得厉害。源码包如果自带权重直接用如果要自己换模型注意输入尺寸和输出格式要跟后处理对上这是二次开发第一个要核对的地方。2.3 目录结构长什么样一个典型的行人检测视频源码包目录不会太复杂常见结构是这样pedestrian-detection/ ├── main.py # 入口串起读视频、检测、写视频 ├── detector.py # 检测器封装加载模型、出框 ├── utils.py # 画框、坐标映射、NMS 等工具函数 ├── models/ # 模型权重或配置文件 ├── videos/ # 输入视频放这里 └── outputs/ # 输出视频落这里这个结构的好处是职责清楚main.py只管流程detector.py只管推理utils.py放可复用的杂活。二次开发时想换模型改detector.py想改画框样式改utils.py互不干扰。如果你的包结构跟这个不一样先花两分钟把每个文件对应到上面四层里后面改起来就不容易乱。3. 跑起来从环境安装到输出第一段带框视频3.1 环境准备与依赖安装先把环境弄干净。我一般用虚拟环境避免跟系统里的包打架python -m venv venv source venv/bin/activate # Windows 用 venv\Scripts\activate pip install opencv-python numpy如果源码包用的是深度学习框架的推理接口还要装对应的包常见的是pip install onnxruntime或框架自带的推理库。装完先验证 OpenCV 能用import cv2 print(cv2.__version__) cap cv2.VideoCapture(0) # 0 是默认摄像头没有就跳过这行 print(cap.isOpened()) cap.release()cv2.__version__能打印出来、isOpened()返回 True说明视频 IO 这层没问题。这一步别省后面视频读不进来先怀疑的就是这里。依赖版本上OpenCV 大版本之间 API 有差异源码包如果写明了版本就按它来没写就用较新的稳定版遇到VideoWriter相关报错再回退。3.2 核心流程代码逐段讲入口文件的主循环是整个包的骨架逻辑大致是这样import cv2 from detector import Detector # 1. 打开输入视频 cap cv2.VideoCapture(videos/input.mp4) if not cap.isOpened(): raise RuntimeError(视频打不开检查路径和编码) # 2. 读取视频属性用于写出 fps cap.get(cv2.CAP_PROP_FPS) w int(cap.get(cv2.CAP_PROP_FRAME_WIDTH)) h int(cap.get(cv2.CAP_PROP_FRAME_HEIGHT)) # 3. 初始化写出器fourcc 决定编码 fourcc cv2.VideoWriter_fourcc(*mp4v) writer cv2.VideoWriter(outputs/result.mp4, fourcc, fps, (w, h)) # 4. 加载检测器 det Detector(models/detector.onnx) # 5. 逐帧循环 while True: ok, frame cap.read() if not ok: break # 读到结尾 boxes det.detect(frame) # 出框坐标是原图尺度 for x1, y1, x2, y2, score in boxes: cv2.rectangle(frame, (x1, y1), (x2, y2), (0, 255, 0), 2) cv2.putText(frame, f{score:.2f}, (x1, y1 - 5), cv2.FONT_HERSHEY_SIMPLEX, 0.5, (0, 255, 0), 1) writer.write(frame) cap.release() writer.release()逐段说逻辑。第 1 段打开视频isOpened()为假就直接抛错别让它静默失败否则后面循环一次都不进你还以为是检测没效果。第 2 段读fps、宽、高这三个值写视频时要用尤其fps写错了输出视频会快放或慢放。第 3 段VideoWriter_fourcc(*mp4v)指定编码输出后缀是 mp4 就用 mp4v是 avi 常用 XVID编码和后缀对不上是打不开视频的头号原因。第 4 段加载检测器路径按实际改。第 5 段是主循环cap.read()返回(ok, frame)ok为假说明读完了必须 break不然会死循环。检测返回的框我约定成原图坐标画框直接用省去在入口再做映射。detect方法内部一般长这样import cv2 import numpy as np class Detector: def __init__(self, model_path, conf0.4, nms0.45, size416): self.net cv2.dnn.readNetFromONNX(model_path) self.conf conf # 置信度阈值 self.nms nms # NMS 阈值 self.size size # 模型输入边长 def detect(self, frame): h, w frame.shape[:2] # 预处理缩放 归一化 转 blob blob cv2.dnn.blobFromImage( frame, 1/255.0, (self.size, self.size), swapRBTrue, cropFalse) self.net.setInput(blob) outs self.net.forward() boxes self._postprocess(outs, w, h) return boxesconf控制框的准入线调高漏检多、调低误检多nms控制重叠框合并的激进程度size是模型输入边长必须跟权重匹配改小了快但小目标容易丢。blobFromImage里swapRBTrue是因为 OpenCV 读进来是 BGR模型多半按 RGB 训练不转颜色会偏。1/255.0是归一化具体缩放系数看模型要求有的模型要减均值除方差那就得按它的预处理来这一步对不上精度会明显掉。后处理是坐标映射和 NMS 的合体逻辑是先把模型输出解析成原图尺度的框再按置信度过滤最后 NMS 去重。常见写法def _postprocess(self, outs, w, h): boxes, scores [], [] for out in outs: for det in out: score float(det[4]) if score self.conf: continue cx, cy, bw, bh det[0] * w, det[1] * h, det[2] * w, det[3] * h x1, y1 int(cx - bw / 2), int(cy - bh / 2) x2, y2 int(cx bw / 2), int(cy bh / 2) boxes.append([x1, y1, x2 - x1, y2 - y1]) scores.append(score) idx cv2.dnn.NMSBoxes(boxes, scores, self.conf, self.nms) return [(b[0], b[1], b[0] b[2], b[1] b[3], s) for i, b, s in zip(idx, boxes, scores)]这里det[0]到det[3]是中心点和宽高且是相对坐标乘w、h映射回原图。不同模型的输出排列不一样有的直接给左上右下有的给中心宽高拿到新模型第一件事就是打印outs的形状和几个值确认排列再改这段这是我最常提醒别人的地方。NMSBoxes接收的是[x, y, w, h]格式所以中间转了一次返回的索引再还原成左上右下。3.3 运行与结果验证代码就位后把视频放进videos/建好outputs/目录直接跑python main.py跑完看outputs/result.mp4。验证分三步先看能不能播放打不开就是编码或路径问题再看框的位置对不对框偏了是坐标映射问题最后看有没有重复框重复多就调 NMS 阈值。帧率方面可以在循环里加个计时打印每帧耗时心里有个数。普通 CPU 上轻量模型跑到十几到几十帧都算正常具体看分辨率和输入尺寸。4. 避坑与排查视频检测最容易翻车的几个点4.1 输出视频打不开或花屏现象程序跑完没报错但result.mp4双击打不开或者能打开但画面是绿的、花的。原因基本在VideoWriterfourcc 跟文件后缀不匹配或者写出的尺寸跟原帧尺寸不一致。解决是把 fourcc 和后缀对齐mp4 用mp4vavi 用XVID并且VideoWriter的尺寸参数必须等于frame.shape的宽高顺序是(w, h)不是(h, w)这个顺序写反了画面会变形甚至写失败。4.2 检测框整体偏移或大小不对现象框能出来但位置偏、大小不对或者框全挤在左上角。原因通常是坐标映射错了模型输出是相对坐标没乘原图宽高或者模型输入做了 letterbox 填充但后处理没把填充去掉。解决是先打印模型输出的原始值确认是相对还是绝对、是中心宽高还是左上右下再对应改映射。用了 letterbox 的要按填充比例反算别直接乘原图尺寸。4.3 视频读进来但循环一次就退出现象while循环只跑一帧就结束输出视频几乎空的。原因是cap.read()第一次就返回okFalse多半是路径错、文件损坏或编码不支持。解决是先单独用VideoCapture打开并read()一次打印ok和frame.shape确认能读到帧再进主流程。路径里有中文或空格也可能出问题换成纯英文路径试。4.4 帧率异常输出视频快放或慢放现象检测没问题但输出视频播放速度明显不对。原因是VideoWriter的 fps 没取原视频的 fps写死了一个默认值。解决是用cap.get(cv2.CAP_PROP_FPS)读原帧率传进去读出来是 0 的话说明容器没记录帧率得手动指定一个合理值。另外逐帧推理慢不会影响输出视频速度因为写出的 fps 是固定的只是处理耗时变长别把这两件事搞混。4.5 换模型后精度骤降现象换了个检测权重框乱七八糟。原因多半是预处理或输出解析跟新模型不匹配比如颜色通道没转、归一化系数不对、输出层排列不同。解决是查新模型的预处理说明逐项对齐blobFromImage的参数再打印输出确认解析方式。这一步没有捷径对齐了精度就回来了。5. 二次开发把 Demo 改成能用的检测流水线5.1 从「能跑」到「好用」的三个改造方向第一个方向是抽帧策略。逐帧检测最稳但最慢实际项目里常见做法是隔帧检测加跟踪补帧或者对静止场景降采样速度能提不少。第二个方向是结果结构化别只写视频把每帧的框坐标、置信度、时间戳写成 JSON 或 CSV后面做统计、告警、回放都用得上。第三个方向是参数外置把置信度、NMS、输入尺寸、输入输出路径挪到配置文件或命令行参数改参数不用动代码这是工程化的基本操作。改造时我一般先加一个配置读取import argparse parser argparse.ArgumentParser() parser.add_argument(--input, defaultvideos/input.mp4) parser.add_argument(--output, defaultoutputs/result.mp4) parser.add_argument(--conf, typefloat, default0.4) parser.add_argument(--nms, typefloat, default0.45) parser.add_argument(--size, typeint, default416) args parser.parse_args() det Detector(models/detector.onnx, args.conf, args.nms, args.size)这样调阈值就是改命令行不用翻代码。--size改小提速、改大提精度--conf和--nms按场景调室内密集场景 NMS 可以适当调高避免框被合并掉。5.2 验证改造是否有效的办法改完别凭感觉用固定视频做回归。准备一段有代表性的视频记录改造前后的检测框数量、平均置信度、每帧耗时对比着看。框数量突然暴涨多半是阈值调太低耗时暴涨多半是输入尺寸调太大。我习惯在循环里加一个简单的统计import time total, frames 0, 0 t0 time.time() # ... 循环内 ... total len(boxes) frames 1 # ... 循环后 ... print(f平均每帧框数 {total/frames:.2f}, f平均耗时 {(time.time()-t0)/frames*1000:.1f} ms)这两个数一出来改造是提速还是降质一目了然。固定视频、固定参数、固定机器三次对比就能判断改动值不值。5.3 一个具体技巧先小后大做参数扫描调参别一上来就全视频跑。我的做法是先从视频里抽 20 到 30 帧存成图片用一个小脚本只跑这些帧快速扫conf和size的组合找到框数和耗时都合适的区间再拿完整视频验证。这样一轮扫描几分钟就出结果比反复跑整段视频省太多时间。抽帧用cap.set(cv2.CAP_PROP_POS_FRAMES, i)定位再read()就行注意有些编码定位不准抽完肉眼扫一眼确认帧没重复。从那以后我每次拿到新的检测源码包都强制先跑通单帧、再跑通短视频、最后才上完整视频参数也先在抽帧集上扫一遍再定。这套顺序看着笨但能省掉大量「跑完一整段才发现框全偏了」的后悔药。希望帮到你。本文还有配套的精品资源点击获取
阅读完成 · 觉得有帮助?