yolov8检测实时视频流,裁剪出未戴头盔的头部方案
要实现 “裁剪出未戴头盔的头部图片,且只包含头部”(不含肩膀、背景、车体等),需要一个 精准定位头部 + 精细裁剪 + 分类判断 的完整流程。
以下是 具体、可落地、高精度的实施方案细节,适用于你当前使用 YOLOv8 的技术栈。
✅ 最终目标
输入:实时视频流
输出:所有被判定为“未戴头盔”的人,其 纯头部区域裁剪图(只含头部,无多余背景)
要求:精准、稳定、可部署
✅ 推荐方案:三阶段流水线(高精度 + 可控性)
[1] YOLOv8n 检测 rider(人车整体) → 定位骑行者↓
[2] 轻量姿态估计(如 MoveNet / YOLO-Pose) → 定位头部关键点↓
[3] 根据关键点生成 tight head bbox → 裁剪 + 分类 → 保存 no_helmet 图像
✅ 优势:头部裁剪精准,只包含头,适合存档、告警、AI回流训练
🔧 分步详解
🟩 第一步:YOLOv8n 检测 rider
(人车整体)
- 模型:
yolov8n.pt
- 类别:
rider
(电动车 + 骑行者整体) - 输出:
bounding box
(x1, y1, x2, y2)
from ultralytics import YOLO
detector = YOLO('best_rider_detect.pt')results = detector(frame)
for r in results:for box in r.boxes:if box.cls == 0: # rider classx1, y1, x2, y2 = box.xyxy[0].cpu().int()rider_crop = frame[y1:y2, x1:x2]
🟩 第二步:在 rider 区域运行轻量姿态估计,定位头部关键点
✅ 推荐模型:MoveNet Lightning 或 YOLOv8-pose-nano
- 轻量、快、适合边缘设备
- 输出 6~17 个关键点(含 nose, eyes, ears, shoulders)
# 使用 Ultralytics YOLOv8-pose
pose_model = YOLO('yolov8n-pose.pt')
pose_results = pose_model(rider_crop, verbose=False)# 获取关键点 (x, y, confidence)
keypoints = pose_results[0].keypoints.xy[0].cpu().numpy() # shape: (17, 2)
confidences = pose_results[0].keypoints.conf[0].cpu().numpy() # shape: (17,)
🎯 关键点索引(COCO 格式):
关键点 | 索引 |
---|---|
Nose(鼻) | 0 |
Left Eye(左眼) | 1 |
Right Eye(右眼) | 2 |
Left Ear(左耳) | 3 |
Right Ear(右耳) | 4 |
🟩 第三步:根据关键点生成“tight head bounding box”
✅ 计算头部区域(只包含头)
import numpy as np# 提取有效关键点(置信度 > 0.5)
valid_points = []
for i in [0, 1, 2, 3, 4]: # nose, eyes, earsif confidences[i] > 0.5:valid_points.append(keypoints[i])if len(valid_points) < 3:continue # 关键点太少,跳过valid_points = np.array(valid_points)# 计算包围所有关键点的最小外接矩形
x_coords = valid_points[:, 0]
y_coords = valid_points[:, 1]head_x1 = int(np.min(x_coords)) - 10 # 左右各扩展 10 像素
head_y1 = int(np.min(y_coords)) - 20 # 向上扩展多些(额头)
head_x2 = int(np.max(x_coords)) + 10
head_y2 = int(np.max(y_coords)) + 5 # 向下到下巴# 映射回原图坐标(因为 rider_crop 是裁剪后的)
head_x1_abs = x1 + head_x1
head_y1_abs = y1 + head_y1
head_x2_abs = x1 + head_x2
head_y2_abs = y1 + head_y2# 边界检查
head_x1_abs = max(0, head_x1_abs)
head_y1_abs = max(0, head_y1_abs)
head_x2_abs = min(frame.shape[1], head_x2_abs)
head_y2_abs = min(frame.shape[0], head_y2_abs)
🟩 第四步:裁剪头部图像并分类
head_crop = frame[head_y1_abs:head_y2_abs, head_x1_abs:head_x2_abs]# 调整大小(分类器输入)
head_resized = cv2.resize(head_crop, (224, 224))# 分类
cls_result = helmet_classifier(head_resized, verbose=False)
pred_label = cls_result[0].names[cls_result[0].probs.top1]# 只保存“未戴头盔”的头部
if pred_label == 'no_helmet':save_path = f'./no_helmet_heads/head_{frame_id}_{obj_id}.jpg'cv2.imwrite(save_path, head_crop) # 保存原始分辨率的头部图
✅ 这样保存的图像 只包含头部,边缘干净,可用于后续分析。
✅ 替代方案(无姿态估计):使用“头部检测模型”+ 匹配
如果你不想用姿态估计,但有“头部”标注数据:
[YOLOv8n 检测 rider] ↓
[YOLOv8n 检测 head](单独训练)↓
IOU 匹配:将 head 框匹配到最近的 rider 框↓
裁剪 head 框 → 分类 → 保存 no_helmet 图像
- 优点:比比例估算精准
- 缺点:双模型,速度慢
✅ 如何提升裁剪质量?
优化项 | 方法 |
---|---|
去模糊 | 保存前判断图像清晰度(Laplacian 方差 > 100) |
去小图 | head_crop 宽高 < 40 像素 → 跳过 |
去遮挡 | 关键点置信度低 → 跳过 |
去重 | 使用 ReID 或 embedding 去除同一人重复保存 |
✅ 输出示例(保存的图像)
no_helmet_heads/
├── head_001.jpg → 纯头部,含脸、头发、耳朵
├── head_002.jpg → 侧脸,只戴安全帽带
└── head_003.jpg → 抬头,额头清晰
✅ 每张图都只包含头部,可用于:
- 违规证据存档
- 安全宣传素材
- 模型回流训练
✅ 部署建议
项目 | 建议 |
---|---|
模型 | YOLOv8n + YOLOv8n-pose + YOLOv8n-cls |
输入尺寸 | 640x640(检测),224x224(分类) |
硬件 | Jetson Orin / RK3588 / PC + GPU |
加速 | ONNX Runtime + TensorRT |
多线程 | 检测、姿态、分类异步流水线 |
✅ 总结:实现“只裁剪头部”的关键
步骤 | 关键点 |
---|---|
1. 定位骑行者 | YOLOv8n 检测 rider |
2. 定位头部 | ✅ 姿态估计(推荐)或 ❌ 比例估算 |
3. 裁剪头部 | 根据关键点生成 tight bbox |
4. 分类判断 | YOLOv8n-cls 判断是否戴头盔 |
5. 保存图像 | 只保存 no_helmet 的 head_crop |
✅ 代码结构建议
main.py
├── detect_rider()
├── estimate_pose(rider_crop)
├── extract_head_bbox(keypoints)
├── classify_helmet(head_crop)
├── save_if_no_helmet(head_crop)
如果你需要,我可以提供:
- 完整 Python 脚本(含摄像头读取、姿态估计、裁剪、保存)
- 如何导出 ONNX 模型加速推理
- 如何打包成边缘设备可运行程序
你现在的目标完全可实现,只需引入“姿态估计”或“头部检测”来精准定位头部,就能完美裁剪出“只含头部”的未戴头盔图像。