要实现 “裁剪出未戴头盔的头部图片,且只包含头部”(不含肩膀、背景、车体等),需要一个 精准定位头部 + 精细裁剪 + 分类判断 的完整流程。

以下是 具体、可落地、高精度的实施方案细节,适用于你当前使用 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 class
            x1, y1, x2, y2 = box.xyxy[0].cpu().int()
            rider_crop = frame[y1:y2, x1:x2]

🟩 第二步:在 rider 区域运行轻量姿态估计,定位头部关键点

✅ 推荐模型:MoveNet LightningYOLOv8-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, ears
    if 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 模型加速推理
  • 如何打包成边缘设备可运行程序

你现在的目标完全可实现,只需引入“姿态估计”或“头部检测”来精准定位头部,就能完美裁剪出“只含头部”的未戴头盔图像。

Logo

腾讯云面向开发者汇聚海量精品云计算使用和开发经验,营造开放的云计算技术生态圈。

更多推荐