基于Python的行人识别系统:从HOG+SVM到YOLO部署调优
2026/9/18 3:57:38 网站建设 项目流程

简介:基于Python的行人识别系统设计与实现,是一份面向计算机视觉方向本科毕业设计的完整学位论文文档。内容围绕行人检测、特征提取与跟踪算法展开,从研究背景、国内外现状到系统框架、模块实现和实验对比均有覆盖,适合需要完成相关课题论文撰写或理解行人识别技术流程的读者参考。文档为docx格式,共1个文件,整体大小为35KB,便于直接下载使用。资源以万字级论文正文为主体,包含摘要、目录、各章节论述及参考文献结构,已做降重处理,可用于论文框架搭建、算法原理解读和写作思路参考。目前已有311人学习下载,兼具实用性与参考价值。该论文还详细说明了HOG、YOLO、Faster R-CNN等常用检测算法,以及SVM、深度学习模型在数据预处理、特征提取和分类训练中的应用,并配套数据集与评价指标介绍,读者可从中获得从系统设计到实验分析的完整闭环思路。

1. 行人识别系统的应用入口与 Python 技术选型

行人识别系统在智能安防、门店客流统计、无人驾驶辅助里都是最先落地的视觉需求。基于 Python 的行人识别系统,核心不是把模型搭出来,而是把相机输入变成可统计的检测框,再决定误报和漏报哪个更不可接受。我见过不少项目把精力全放在训练精度上,忽略了数据集分布、IOU 阈值、帧率换算这些工程参数,结果模型在测试集上好看,上线后业务不可用。这里按一条可复现的路线,把设计、传统 HOG 特征、深度学习迁移、部署调优串起来讲,给正在做课程设计或小型产品原型的开发者作参考。

2. 把系统需求拆成数据流:标注格式、IOU 与训练集设计

碰到“基于 Python 的行人识别系统”这类题目,我通常先不急着选模型,而是先画数据流。一路 RTSP 视频流进来,经过抽帧、检测、跟踪、统计,最后写数据库或告警,链路里每个环节的输入输出必须明确。常见做法是拆成四层:接入层负责解码和抽帧,检测层负责输出[x1, y1, x2, y2, score, class],决策层过滤和关联目标,存储层记录结果。如果目标是统计员工通道人数,检测层可以不 24 小时全帧跑,只在有人进入 ROI 区域时触发高分辨率检测,算力消耗会低一个数量级。

2.1 功能模块与工程目录

一个能维护的项目,目录结构通常长这样:

video_stream/ detector/ hog_svm.py yolo_detector.py tracker/ iou_tracker.py pipeline/ frame_processor.py config/ params.yaml utils/ nms.py

这是模块化后的最小骨架。detector只管输出检测框,tracker根据 IOU 关联前后帧的同一目标,pipeline负责串联,业务逻辑单独放 service 层,不要在检测脚本里写告警余量判断。下表是每一层需要守住的接口边界:

模块责任边界输出格式
接入层解码、抽帧、分辨率归一化BGR frame
检测层目标定位与分类[x1, y1, x2, y2, score, class]
决策层区域过滤、置信度过滤结构化事件
存储层结果落库或截图JSON、数据库记录

这样设计的好处是以后换检测算法只替换detector内部实现,上层接口不变。无论项目是课程设计还是产品原型,这一层抽象直接决定后期改造成本。实际开发中我还会在config/params.yaml里单独保存检测阈值、ROI 坐标和抽帧间隔,避免每次调参都改代码。

2.2 数据集标注与 IOU 的判定标准

行人识别属于监督学习,数据集决定精度上限。用 LabelImg 标注时,常见错误是把人体框到紧贴身体,导致框的高宽比不稳定。稳妥做法是统一采用直立行人框:左边界从腋下开始,右边界到腋下,上边界接近头顶留 2 到 3 像素,下边界到鞋底。编码时先定义数据结构:

from dataclasses import dataclass @dataclass class BBox: class_id: int score: float = 1.0 x1: int = 0 y1: int = 0 x2: int = 0 y2: int = 0 def area(self) -> int: return max(0, self.x2 - self.x1) * max(0, self.y2 - self.y1) def compute_iou(a: BBox, b: BBox) -> float: ix1 = max(a.x1, b.x1); iy1 = max(a.y1, b.y1) ix2 = min(a.x2, b.x2); iy2 = min(a.y2, b.y2) iw = max(0, ix2 - ix1); ih = max(0, iy2 - iy1) inter = iw * ih union = a.area() + b.area() - inter return inter / union if union > 0 else 0.0

compute_iou的返回值在训练指标和 NMS 里都会用到:mAP 计算默认把 IOU=0.5 作为正样本阈值,而 NMS 的 IOU 阈值通常会设得更高,比如 0.45。两者的用途不同,不要混用。如果发现训练集里标注框大面积互相重叠,先检查数据采集的时间间隔是否过大,同一行人出现两个半身框,这种质量噪声比模型参数量更能决定最终精度。

2.3 数据增强:先解决样本不平衡再谈网络结构

行人识别最常见的失败是漏检穿深色衣服、遮挡和俯拍视角。常见做法是复制正样本后做左右翻转、随机裁剪和 HSV 扰动,但有一条必须注意:增强后的样本要避开物理上不合理的组合。右向左行走的人翻成左向右没问题,俯拍视角下按 90 度旋转就可能生成现实中不存在的姿态。使用 albumentations 或 torchvision 的 transform 都行,关键参数我会这样设:亮度扰动范围 ±20%,对比度 ±10%,随机裁剪比例 0.8 到 1.0,避免行人被截断过半。负样本建议单独留一批不裁剪的原图,防止模型通过背景纹理识别人。

3. 基于 Python 的 HOG+SVM 最快跑通行人识别系统

3.1 为什么老牌 HOG 仍然值得写在第一版

HOG 统计图像局部梯度方向,对边缘和轮廓敏感。行人在直立姿态下轮廓特征稳定,所以 HOG+SVM 仍是 CPU 成本最低的方案。OpenCV 的HOGDescriptor直接提供了特征提取和检测接口,不需要自己实现梯度直方图。如果系统只需要在固定机位、固定视角下检测行人,HOG+SVM 在 1080P 图像上也能跑出可接受的实时性,适合作为没有 GPU 环境时的第一版。

3.2 训练分类器的骨干代码与参数

用 OpenCV 训练自己的行人分类器需要两步:从训练图片提取 HOG 特征,再喂给 SVM。下面的代码把样本统一缩放到 64x128,特征维度约 3780:

import cv2 import numpy as np from sklearn.svm import LinearSVC # winSize, blockSize, blockStride, cellSize, nbins hog = cv2.HOGDescriptor((64, 128), (16, 16), (8, 8), (8, 8), 9) def extract_features(imgs): features = [] for img in imgs: img = cv2.resize(img, (64, 128)) gray = cv2.cvtColor(img, cv2.COLOR_BGR2GRAY) features.append(hog.compute(gray).flatten()) return np.array(features, dtype=np.float32) X = extract_features(pos_imgs + neg_imgs) y = np.array([1] * len(pos_imgs) + [0] * len(neg_imgs)) # 线性 SVM,特征维度高,径向基核容易过拟合 svm = LinearSVC(C=0.01, max_iter=10000) svm.fit(X, y)

注意图片必须统一用cv2.resize,不能直接让 HOG 拿到任意大小输入。LinearSVC的正则化参数C对结果影响很大:正样本数量少时,C从 0.01 降到 0.001 能减少过拟合。下面是这套 HOG 参数的实际含义,改动任何一个都会影响特征维度和检测速度:

参数推荐值作用边界
winSize(64, 128)检测窗口,对应行人高宽比约 2:1
blockSize(16, 16)局部归一化尺寸,过大容易丢失细节
blockStride(8, 8)区块滑动的重叠步长,越小特征越多
cellSize(8, 8)梯度直方图单元,通常固定 8x8
nbins9梯度方向分箱,9 是经典取值

3.3 滑窗与 NMS 非极大值抑制

HOG+SVM 检测时会在整张图上做多尺度滑窗,同一个行人会被多个窗口命中,最后必须用 NMS 合并。NMS 的核心逻辑是:按得分排序,保留得分最高的框,然后删掉所有与它 IOU 超过阈值的框,再取下一个。实现如下:

def nms(boxes, scores, iou_threshold=0.4): order = np.argsort(scores)[::-1] keep = [] while order.size > 0: i = order[0] keep.append(i) xs1 = np.maximum(boxes[i, 0], boxes[order[1:], 0]) ys1 = np.maximum(boxes[i, 1], boxes[order[1:], 1]) xs2 = np.minimum(boxes[i, 2], boxes[order[1:], 2]) ys2 = np.minimum(boxes[i, 3], boxes[order[1:], 3]) w = np.maximum(0, xs2 - xs1) h = np.maximum(0, ys2 - ys1) inter = w * h union = (boxes[i, 2] - boxes[i, 0]) * (boxes[i, 3] - boxes[i, 1]) \ + (boxes[order[1:], 2] - boxes[order[1:], 0]) * (boxes[order[1:], 3] - boxes[order[1:], 1]) - inter iou = inter / np.maximum(union, 1e-6) order = order[1 + np.where(iou <= iou_threshold)[0]] return keep

这里的iou_threshold越小,保留的框越少,能压住重叠误检,但也会让紧挨着的两个人被误合并成一个目标。处理密集人群时,我会把这个阈值放到 0.3 到 0.5 之间观察,并用实际视频片段验证,不要只对着单张图片调。HOG+SVM 的得分严格说不是概率,而是 SVM 超平面距离,所以分类器阈值通常从 0 开始往正方向调,比如 0.2。

4. 把识别精度做到可落地:PyTorch 与 YOLO 的迁移学习

4.1 选型:YOLO 与 Faster R-CNN 的边界

行程识别系统要覆盖遮挡、拥挤、不同相机角度,HOG+SVM 很快会到瓶颈,这时需要转到深度学习。常用方案分为两派:两阶段检测器 Faster R-CNN 和一阶段的 YOLO 系列。它们不是精度和速度的简单二选一,而是工程复杂度的取舍:

方案优点缺点适用场景
Faster R-CNN小目标召回率高,精度稳定推理慢,部署依赖多离线视频分析
YOLO 系列推理快,生态完善,部署资料多小目标仍需要抠细节实时视频流
RT-DETR端到端,免去 NMS 后处理训练成本高,资料少新项目预研

如果只需要识别“person”一个类别,我一般直接选择轻量 YOLO 版本。原因不是它一定比 Faster R-CNN 准,而是 PyTorch 生态里对 YOLO 的导出、量化、TensorRT 加速支持最成熟,能少走很多工程弯路。

4.2 YOLO 最小推理脚本

拿到预训练权重后,不需要从零开始训练,直接推理即可验证效果。常见做法是用ultralytics包加载权重:

import cv2 from ultralytics import YOLO # yolov8s.pt 是带了 COCO 预训练权重的模型文件 model = YOLO("yolov8s.pt") cap = cv2.VideoCapture("test.mp4") while cap.isOpened(): ret, frame = cap.read() if not ret: break # conf 控制保留哪些框,iou 是 NMS 阈值 results = model.predict(frame, conf=0.35, iou=0.45, imgsz=640) boxes = results[0].boxes.xyxy.cpu().numpy() scores = results[0].boxes.conf.cpu().numpy() classes = results[0].boxes.cls.cpu().numpy() # COCO 数据集中类别 0 对应 person person_boxes = boxes[classes == 0] person_scores = scores[classes == 0]

conf=0.35表示只有置信度大于 0.35 的框才输出,iou=0.45是 NMS 合并阈值,imgsz=640在保证精度的同时兼顾速度。实际部署时,我会把imgsz设成 640 或 1280,不建议直接用默认值而不看模型输入尺寸。如果视频里行人很小,imgsz=1280能显著提升召回,但推理时间接近翻倍,需要通过conf阈值做补偿。

4.3 迁移学习的关键操作

业务场景里的行人往往和 COCO 预训练分布不同,比如高架摄像头俯拍。迁移学习最有效的方法是冻结主干,只训练检测头:

from ultralytics import YOLO model = YOLO("yolov8s.pt") # 冻结 backbone,只训练 head 部分 results = model.train( data="pedestrian.yaml", epochs=30, freeze=10, batch=16, imgsz=640, lr0=0.001, )

freeze=10表示冻结模型前 10 层特征提取层,保留通用特征,后面的层用自定义数据微调。训练时我会先跑 10 个 epoch 观察损失曲线,如果训练损失持续下降但验证损失不降,说明过拟合,立即停止并减小lr0到 0.0005。不要把训练直接交到默认参数手里,损失值在 0.1 以下时初学者容易过拟合,真正要看的是验证集 mAP 是否还在上升。

5. 把基于 Python 的行人识别系统部署到业务:三个必调参数与 mAP 验证

5.1 导出 ONNX 与加速推理

模型训练完成后,第一步是导出成 ONNX,避免生产环境依赖完整 PyTorch 库:

yolo export model=yolov8s.pt format=onnx imgsz=640 half=True

half=True会把权重转成半精度,显存占用减半。导出后可以用 ONNX Runtime 加载,在 CPU 和 GPU 上都能运行,不依赖 PyTorch 版本。部署时不要直接调用模型目录里的 Python 脚本,业务代码里只保留 ONNX Session 的run接口。

5.2 三个必调参数:conf、IoU 和帧率降采样

上线后的调参与训练不同,以下三个参数几乎决定业务可用性:

参数推荐区间调参方向
conf 阈值0.25 ~ 0.5误报多就调高,漏检多就调低
NMS IoU 阈值0.4 ~ 0.6人群密集时调低,行人稀疏时调高
抽帧间隔1 ~ 5 帧按行人通过速度调节,取多帧反而产生重复告警

我给线上接口建议的做法是:先固定 conf=0.35,用一段 10 分钟真实视频记录漏检与误报,再反向修正。很多人会在视频里逐帧检测,导致同一个行人被重复计数,原因不是模型差,而是没有设置抽帧间隔或者没有跟踪关联。加一个 IOU 跟踪器,把相邻帧检测框的 IOU 大于 0.3 的目标视为同一行人,计数才会稳定。

5.3 用 mAP 和 FPS 同时判断系统健康度

模型是否值得上线,至少要看两个指标:模型精度 mAP 和系统吞吐 FPS。mAP 的计算流程是依次枚举置信度阈值,以 IOU=0.5 判定正负样本,画 PR 曲线取积分。不要只依赖训练日志里的 mAP,那是在固定测试集上算出来的,业务画面的分布可能差别很大。我通常会在推理脚本里挂一个可视化窗口,按随机间隔保存检测结果到独立文件夹,每天抽看一次。最后补一个很有价值的步骤:把漏检片段单独抽出来重新标注,加入训练集,重复两到三轮。这个长尾闭环比反复调置信度阈值更能提升实际效果。

本文还有配套的精品资源,点击获取

需要专业的网站建设服务?

联系我们获取免费的网站建设咨询和方案报价,让我们帮助您实现业务目标

立即咨询