昇腾AI行业案例(二):基于 AI 图像处理的安全帽检测

00 - 前言

欢迎学习《昇腾行业应用案例》的 “基于 AI 图像处理的安全帽检测” 实验。在本实验中,你将深入了解如何运用计算机视觉(CV)领域的 AI 模型,搭建一个高效精准的安全帽检测系统,并利用开源数据集对模型效果加以验证。为此,我们将使用昇腾的AI硬件以及CANN等软件产品。

学习目标


在本课程中,您将学习一些与使用AI图像处理技术实现安全帽检测有关的重要概念,包括:

  • 图像数据的预处理方法
  • 采用计算机视觉检测人物和安全帽的方法
  • 目标检测后处理的方法
  • 端到端深度学习工作流

目录


本实验分为四个核心部分。第一部分主要介绍案例的应用场景,第二部分会详细阐述端到端的解决方案,搭建起技术实现的整体框架。第三部分会手把手指导您完成代码编写与实现,最后一部分给出测试题,帮助您巩固学习内容。

  1. 场景介绍
  2. 解决方案
  3. 代码实战
  4. 课后测试

JupyterLab

在本实操实验中,我们将使用 JupyterLab 管理我们的环境。JupyterLab 界面是一个控制面板,可供您访问交互式 iPython Notebook、所用环境的文件夹结构,以及用于进入 Ubuntu 操作系统的终端窗口,只需要点击菜单栏的小三角就可以运行代码。

尝试执行下方单元中的一条简单的 print(打印)语句。

# DO NOT CHANGE THIS CELL
# activate this cell by selecting it with the mouse or arrow keys then use the keyboard shortcut [Shift+Enter] to execute
print('This is just a simple print statement')
This is just a simple print statement

01 场景介绍

在各类建筑工地、工厂车间等生产作业场所,安全问题始终是重中之重。安全帽作为保护工作人员头部免受意外伤害的关键防护装备,正确佩戴与否直接关乎生命安全。然而,现实场景中,因人员疏忽、管理不善等因素,未佩戴或未正确佩戴安全帽的情况屡见不鲜,这无疑极大地增加了安全事故发生的风险。

据相关统计数据显示,在建筑行业,因头部受伤导致的伤亡事故占比颇高。每年,全球范围内都有众多工人因未规范佩戴安全帽,在高空坠物、设备碰撞等意外发生时,遭受重创甚至失去生命。这些惨痛的教训凸显出安全帽检测技术的迫切性与重要性。
随着 AI 图像处理技术与深度学习的飞速发展,企业得以借助先进的 AI 技术实现作业现场的实时监控,精准、快速地判断工人是否正确佩戴安全帽。这项技术通过分析现场摄像头采集的图像,对画面中的人头进行定位识别,再细致判断安全帽的佩戴情况,一经发现违规现象,立即触发警报,通知现场管理人员及时纠正,从而有效预防安全事故的发生,为安全生产保驾护航。

02 解决方案

安全帽检测已然成为众多企业安全生产监控系统中的关键一环。典型的安全帽检测方案主要包含以下几大模块:

file
  1. 图像预处理模块:负责接收现场摄像头实时传输的图像数据,进行降噪、增强、尺寸调整等预处理操作,提升图像质量,剔除干扰信息,为后续模型处理奠定良好基础。
  2. 人物检测模型和头盔检测模型:运用CV领域成熟的目标检测模型,例如本课程选用的 YOLOv5 模型,精准定位图像中的人物位置和头盔位置,判断所有人物是否有佩戴安全帽。
  3. 目标检测后处理模块:目标检测模型的输出结果通常会包含很多重复框,为了方便我们进行结果分析,我们需要对目标检测结果去重,主要使用的是 non_max_suppression 算法。

2.1 图像预处理模块

在真实业务场景中,需要连续分析多帧图像,所以需要构建一个图片管理器,持续给AI模型输送图片。此外,原始图片的形状、像素值往往和AI模型的输入要求不一致,所以我们需要对原始图片进行处理,包括 resizetransposepadding 等操作。

2.2 目标检测模型

目标检测算法历经传统机器学习与 AI 视觉两个发展阶段。早期的机器学习模型多依赖手工设计特征,像 Haar 特征结合 AdaBoost 分类器,或是基于 HOG(Histogram of Oriented Gradients)特征与 SVM(Support Vector Machine)的组合。这些方法在简单场景下有一定效果,但面对复杂多变的施工现场,光照不均、人员密集、遮挡严重等问题使其泛化能力大打折扣,检测精度与效率难以满足需求。深度学习兴起后,基于卷积神经网络(CNN)的目标检测算法渐成主流,YOLOv5 模型便是其中佼佼者,凭借出色的实时性与准确性,广泛应用于目标检测任务。

file

YOLOv5 模型的主干网络采用的是 CSPDarknet53 结构,能够高效提取图像多尺度特征,为后续检测夯实基础,快速捕捉人物和头盔关键信息。Neck 部分引入了特征金字塔网络(FPN)与 PAN(Path Aggregation Network)结构,可以融合不同层次特征图,增强模型对不同大小目标物体的检测能力,确保小目标也无处遁形。YOLOv5 的 Head 部分负责最终分类与边界框回归任务,依据 Neck 传来的特征图,精准输出检测结果,框定目标位置,附带置信度信息。本课程选用 YOLOv5 模型有诸多优势:高准确性,经大量数据训练优化,精准定位人物和头盔;实时性能佳,满足施工现场实时监控需求;多尺度检测能力强,适配不同距离、大小的目标;模型结构紧凑,便于部署与优化。

03 动手实验

3.1 实验准备

数据集准备

实验所用图像数据源自公开的建筑工地监控数据集,该数据集收纳了海量工人施工场景图片,且均精准标注了安全帽佩戴情况,本实验选取了少量图片,存放于 ./test_data 目录下。

模型权重

本实验采用的 YOLOv5_s 模型权重可从GitHub 仓库下载,确保其适配安全帽佩戴判断任务;下载解压后,妥善置于指定目录,方便后续代码快速加载与调用。
万事俱备,接下来让我们正式步入代码实操环节!

3.2 构建图像处理类

从测试图片目录读取图像,构建 Python 类封装图像预处理流程,实现代码复用与高效管理。
首先,导入所需三方库:

import cv2
import glob
import numpy as np
from pathlib import Path

创建图像预处理类 LoadImages:

class LoadImages:
    def __init__(self, path, img_size=640, stride=32, auto=True):
        """
        Initializes YOLOv5 loader for images, supporting directories.
        
        Args:
            path (str): Directory path containing images.
            img_size (int): Image size for resizing.
            stride (int): Stride for resizing.
            auto (bool): Automatic image resizing.
        """
        self.img_size = img_size
        self.stride = stride
        self.auto = auto
        self.files = sorted(glob.glob(str(Path(path).resolve()) + '/*.*'))  # directory

        self.nf = len(self.files)  # number of files
        assert self.nf > 0, f"No images found in {path}. Supported formats are: {IMG_FORMATS}"

    def __iter__(self):
        """Initializes iterator by resetting count and returns the iterator object itself."""
        self.count = 0
        return self

    def __next__(self):
        """Advances to the next file in the dataset, raising StopIteration if at the end."""
        if self.count == self.nf:
            raise StopIteration
        path = self.files[self.count]

        self.count += 1
        im0 = cv2.imread(path)  # BGR
        assert im0 is not None, f"Image Not Found {path}"

        # Padded resize
        im = letterbox(im0, self.img_size, stride=self.stride, auto=self.auto)[0]  # padded resize
        im = im.transpose((2, 0, 1))[::-1]  # HWC to CHW, BGR to RGB
        im = np.ascontiguousarray(im)  # contiguous

        return path, im, im0

其中的 letterbox 函数用于对图像进行填充:

def letterbox(im, new_shape=(640, 640), color=(114, 114, 114), auto=True, scaleFill=False, scaleup=True, stride=32):
    """Resizes and pads image to new_shape with stride-multiple constraints, returns resized image, ratio, padding."""
    shape = im.shape[:2]  # current shape [height, width]
    if isinstance(new_shape, int):
        new_shape = (new_shape, new_shape)

    # Scale ratio (new / old)
    r = min(new_shape[0] / shape[0], new_shape[1] / shape[1])
    if not scaleup:  # only scale down, do not scale up (for better val mAP)
        r = min(r, 1.0)

    # Compute padding
    ratio = r, r  # width, height ratios
    new_unpad = int(round(shape[1] * r)), int(round(shape[0] * r))
    dw, dh = new_shape[1] - new_unpad[0], new_shape[0] - new_unpad[1]  # wh padding
    if auto:  # minimum rectangle
        dw, dh = np.mod(dw, stride), np.mod(dh, stride)  # wh padding
    elif scaleFill:  # stretch
        dw, dh = 0.0, 0.0
        new_unpad = (new_shape[1], new_shape[0])
        ratio = new_shape[1] / shape[1], new_shape[0] / shape[0]  # width, height ratios

    dw /= 2  # divide padding into 2 sides
    dh /= 2

    if shape[::-1] != new_unpad:  # resize
        im = cv2.resize(im, new_unpad, interpolation=cv2.INTER_LINEAR)
    top, bottom = int(round(dh - 0.1)), int(round(dh + 0.1))
    left, right = int(round(dw - 0.1)), int(round(dw + 0.1))
    im = cv2.copyMakeBorder(im, top, bottom, left, right, cv2.BORDER_CONSTANT, value=color)  # add border
    return im, ratio, (dw, dh)

测试该类,读取并预处理图像:

source = "./test_data/"
dataset = LoadImages(source)
for path, im, im0 in dataset:
    print(im.shape)
(3, 448, 640)
(3, 640, 416)

3.3 使用 YOLOv5 模型检测人物和头盔

依据 2.2 章节知识,用 YOLOv5_s 模型精准定位人头。
首先,我们需要在服务器的工作目录克隆模型代码,并且把代码仓路径添加到python环境,便于我们调用模型:

# git clone https://github.com/ultralytics/yolov5
# export PYTHONPATH="./yolov5/:$PYTHONPATH"

此外,还需要下载yolov5_s的预训练权重 helmet_head_person_s.pt
接下来测试一下模型的推理功能:

from models.common import DetectMultiBackend  # 导入yolov5_s模型
import torch
import torch_npu  # 使用昇腾硬件推理
from torch_npu.contrib import transfer_to_npu  # 使用昇腾硬件推理
import warnings
warnings.filterwarnings('ignore')
model = DetectMultiBackend("./helmet_head_person_s.pt")
img_loader = LoadImages('./test_data')
for path, im, im0s in img_loader:
    im = torch.from_numpy(im)
    im = im.half() if model.fp16 else im.float()  # uint8 to fp16/32
    im /= 255  # 0 - 255 to 0.0 - 1.0
    if len(im.shape) == 3:
        im = im[None]  # expand for batch dim
    pred = model(im)[0]
    print(pred.shape)
Fusing layers... 
YOLOv5s summary: 232 layers, 7251912 parameters, 0 gradients


torch.Size([1, 17640, 8])
torch.Size([1, 16380, 8])

3.4 目标检测结果的后处理流程

正如前面提到的,模型输出的检测框可能存在大量重复的情况,所以我们需要实现 non_max_suppression 算法及其相关的支撑函数,来对这些结果进行过滤和优化。
在实现 non_max_suppression 之前,我们需要先实现2个依赖函数,xywh2xyxy 用来把检测框的 [x, y, w, h] 坐标转成 [x1, y1, x2, y2] 坐标,box_iou 用来计算检测框之间的重合度。

# (x, y, w, h)的坐标格式转成(x1, y1, x2, y2)
def xywh2xyxy(x):
    """Convert nx4 boxes from [x, y, w, h] to [x1, y1, x2, y2] where xy1=top-left, xy2=bottom-right."""
    y = x.clone() if isinstance(x, torch.Tensor) else np.copy(x)
    y[..., 0] = x[..., 0] - x[..., 2] / 2  # top left x
    y[..., 1] = x[..., 1] - x[..., 3] / 2  # top left y
    y[..., 2] = x[..., 0] + x[..., 2] / 2  # bottom right x
    y[..., 3] = x[..., 1] + x[..., 3] / 2  # bottom right y
    return y

# 计算方框之间的 IOU(Intersection over Union)
def box_iou(box1, box2, eps=1e-7):
    # https://github.com/pytorch/vision/blob/master/torchvision/ops/boxes.py
    """
    Return intersection-over-union (Jaccard index) of boxes.

    Both sets of boxes are expected to be in (x1, y1, x2, y2) format.

    Arguments:
        box1 (Tensor[N, 4])
        box2 (Tensor[M, 4])

    Returns:
        iou (Tensor[N, M]): the NxM matrix containing the pairwise
            IoU values for every element in boxes1 and boxes2
    """
    # inter(N,M) = (rb(N,M,2) - lt(N,M,2)).clamp(0).prod(2)
    (a1, a2), (b1, b2) = box1.unsqueeze(1).chunk(2, 2), box2.unsqueeze(0).chunk(2, 2)
    inter = (torch.min(a2, b2) - torch.max(a1, b1)).clamp(0).prod(2)

    # IoU = inter / (area1 + area2 - inter)
    return inter / ((a2 - a1).prod(2) + (b2 - b1).prod(2) - inter + eps)

接下来,我们就可以实现非极大抑制算法了。这个算法的步骤如下:

file
  1. 置信度排序:
    对于每个图像,NMS算法首先根据检测到的对象的置信度(confidence)进行排序。置信度通常是指模型对于检测到的对象属于某个类别的概率。
  2. 选择最佳边界框:
    选择置信度最高的边界框,将其作为参考框(reference box)。
  3. 计算交并比(IoU):
    计算参考框与其他所有边界框的交并比(Intersection over Union,IoU)。IoU是一个衡量两个边界框重叠程度的指标,计算公式为两个边界框交集面积与并集面积的比值。
  4. 抑制重叠边界框:
    如果某个边界框与参考框的IoU超过了预设的阈值(iou_thres),则认为这两个边界框重叠过多,将该边界框抑制(即从候选列表中移除)。
  5. 迭代处理:
    在移除所有与当前参考框重叠过多的边界框后,选择置信度次高的边界框作为新的参考框,重复步骤3和4,直到所有边界框都被处理。
  6. 输出结果:
    经过NMS处理后,剩下的边界框即为最终的检测结果。

代码实现如下:

# 非极大抑制去除重复框
def non_max_suppression(
    prediction,
    conf_thres=0.25,
    iou_thres=0.45,
    classes=None,
    agnostic=False,
    multi_label=True,
    labels=(),
    max_det=300,
    nm=0,  # 掩码的数量
):
    """
    在推理结果上执行非最大抑制(NMS)以拒绝重叠的检测结果。

    返回:
         检测结果的列表,每个图像对应一个(n,6)张量 [x1, y1, x2, y2, conf, cls]
    """
    # 入参检查
    assert 0 <= conf_thres <= 1, f"置信度阈值 {conf_thres} 无效,有效值应在0.0和1.0之间"
    assert 0 <= iou_thres <= 1, f"IoU {iou_thres} 无效,有效值应在0.0和1.0之间"
    if isinstance(prediction, (list, tuple)):  # YOLOv5模型在验证模型中,输出 = (推理输出, 损失输出)
        prediction = prediction[0]  # 仅选择推理输出

    bs = prediction.shape[0]  # 批量大小
    nc = prediction.shape[2] - nm - 5  # 类别数量
    xc = prediction[..., 4] > conf_thres  # 候选框

    # 设置超参
    max_wh = 7680  # (像素)最大框宽和高
    max_nms = 30000  # 传入torchvision.ops.nms()的最大框数
    time_limit = 0.5 + 0.05 * bs  # 超时时间
    redundant = True  # 需要冗余检测
    multi_label &= nc > 1  # 每个框多个标签(增加0.5ms/图像)
    merge = False  # 不使用合并NMS

    t = time.time()
    mi = 5 + nc  # 掩码起始索引
    output = [torch.zeros((0, 6 + nm), device=prediction.device)] * bs  # 初始化输出检测框
    for xi, x in enumerate(prediction):  # 遍历推理检测结果
        x = x[xc[xi]]  # 获取置信度
        # 如果自动标记,则添加先验标签
        if labels and len(labels[xi]):
            lb = labels[xi]
            v = torch.zeros((len(lb), nc + nm + 5), device=x.device)
            v[:, :4] = lb[:, 1:5]  # 框
            v[:, 4] = 1.0  # 置信度
            v[range(len(lb)), lb[:, 0].long() + 5] = 1.0  # 类别
            x = torch.cat((x, v), 0)

        # 如果没有剩余的处理下一张图像
        if not x.shape[0]:
            continue

        # 计算置信度
        x[:, 5:] *= x[:, 4:5]  # 置信度 = 目标置信度 * 类别置信度

        # 框/掩码
        box = xywh2xyxy(x[:, :4])  # (中心x, 中心y, 宽度, 高度) 转换为 (x1, y1, x2, y2)
        mask = x[:, mi:]  # 如果没有掩码则为零列

        # 检测矩阵 nx6 (xyxy, conf, cls)
        if multi_label:
            i, j = (x[:, 5:mi] > conf_thres).nonzero(as_tuple=False).T
            x = torch.cat((box[i], x[i, 5 + j, None], j[:, None].float(), mask[i]), 1)
        else:  # 仅最佳类别
            conf, j = x[:, 5:mi].max(1, keepdim=True)
            x = torch.cat((box, conf, j.float(), mask), 1)[conf.view(-1) > conf_thres]

        # 按类别过滤
        if classes is not None:
            x = x[(x[:, 5:6] == torch.tensor(classes, device=x.device)).any(1)]

        # 检查形状
        n = x.shape[0]  # 框的数量
        if not n:  # 没有框
            continue

        x = x[x[:, 4].argsort(descending=True)[:max_nms]]  # 按置信度排序并移除多余的框

        # 批量NMS
        c = x[:, 5:6] * (0 if agnostic else max_wh)  # 类别
        boxes, scores = x[:, :4] + c, x[:, 4]  # 框(按类别偏移), 得分
        
        # 调用torchvision封装好的NMS接口
        i = torchvision.ops.nms(boxes, scores, iou_thres)  # NMS
        i = i[:max_det]  # 限制检测数量
        if merge and (1 < n < 3e3):  # 合并NMS(使用加权平均合并框)
            # 更新框为 boxes(i,4) = weights(i,n) * boxes(n,4)
            iou = box_iou(boxes[i], boxes) > iou_thres  # IoU矩阵
            weights = iou * scores[None]  # 框权重
            x[i, :4] = torch.mm(weights, x[:, :4]).float() / weights.sum(1, keepdim=True)  # 合并框
            if redundant:
                i = i[iou.sum(1) > 1]  # 需要冗余

        output[xi] = x[i]
        if (time.time() - t) > time_limit:
            LOGGER.warning(f"警告 ⚠️ NMS 时间限制 {time_limit:.3f}s 超过")
            break  # 时间限制超过

    return output

此外,我们还需要设计一个函数对检测框的坐标进行缩放,原因是我们在推理时为了让图片能够被AI模型处理,对它的形状进行了改变。为了让检测框准确地描绘在原来的图片上,我们需要时缩放函数 scale_boxes 及其支撑函数 clip_boxes

def clip_boxes(boxes, shape):
    """Clips bounding box coordinates (xyxy) to fit within the specified image shape (height, width)."""
    if isinstance(boxes, torch.Tensor):  # faster individually
        boxes[..., 0].clamp_(0, shape[1])  # x1
        boxes[..., 1].clamp_(0, shape[0])  # y1
        boxes[..., 2].clamp_(0, shape[1])  # x2
        boxes[..., 3].clamp_(0, shape[0])  # y2
    else:  # np.array (faster grouped)
        boxes[..., [0, 2]] = boxes[..., [0, 2]].clip(0, shape[1])  # x1, x2
        boxes[..., [1, 3]] = boxes[..., [1, 3]].clip(0, shape[0])  # y1, y2
        
def scale_boxes(img1_shape, boxes, img0_shape, ratio_pad=None):
    """Rescales (xyxy) bounding boxes from img1_shape to img0_shape, optionally using provided `ratio_pad`."""
    if ratio_pad is None:  # calculate from img0_shape
        gain = min(img1_shape[0] / img0_shape[0], img1_shape[1] / img0_shape[1])  # gain  = old / new
        pad = (img1_shape[1] - img0_shape[1] * gain) / 2, (img1_shape[0] - img0_shape[0] * gain) / 2  # wh padding
    else:
        gain = ratio_pad[0][0]
        pad = ratio_pad[1]

    boxes[..., [0, 2]] -= pad[0]  # x padding
    boxes[..., [1, 3]] -= pad[1]  # y padding
    boxes[..., :4] /= gain
    clip_boxes(boxes, img0_shape)
    return boxes

最后,我们还需要设计一个函数,用来分析检测到的人物和头盔,看看谁没有带头盔:

# 找到没有佩戴安全帽的人
def find_person_without_helmet(detections):
    person_without_helmet = []
    # 将检测结果按类别分开存储
    person_detections = []
    helmet_detections = []
    
    for det in detections:
        x1, y1, x2, y2, confidence, label = det
        if label == 0:  # 'person'
            person_detections.append((x1, y1, x2, y2))
        elif label == 2:  # 'helmet'
            helmet_detections.append((x1, y1, x2, y2))
    
    # 检查每个'person'检测框是否包含'helmet'
    for person_box in person_detections:
        person_x1, person_y1, person_x2, person_y2 = person_box
        person_area = (person_x2 - person_x1) * (person_y2 - person_y1)
        
        # 检查是否有'helmet'的90%区域落在'person'检测框内
        contains_helmet = False
        for helmet_box in helmet_detections:
            helmet_x1, helmet_y1, helmet_x2, helmet_y2 = helmet_box
            helmet_area = (helmet_x2 - helmet_x1) * (helmet_y2 - helmet_y1)
            
            # 计算'helmet'和'person'的交集面积
            intersection_x1 = max(person_x1, helmet_x1)
            intersection_y1 = max(person_y1, helmet_y1)
            intersection_x2 = min(person_x2, helmet_x2)
            intersection_y2 = min(person_y2, helmet_y2)
            
            intersection_area = max(0, intersection_x2 - intersection_x1) * max(0, intersection_y2 - intersection_y1)
            
            # 检查'helmet'的90%是否在'person'内
            if intersection_area / helmet_area >= 0.9:
                contains_helmet = True
                break
        
        # 如果当前'person'不包含'helmet'
        if not contains_helmet:
            person_without_helmet.append((person_x1, person_y1, person_x2, person_y2))
    
    return person_without_helmet

3.5 整合代码实现端到端检测

为达成施工现场安全帽佩戴情况的实时、精准监测,需将前文的图像预处理、安全帽佩戴判断等环节代码有机整合,封装成完整的端到端检测流程。

from ultralytics.utils.plotting import Annotator, colors
import time
import torchvision

conf_thres=0.25  # confidence threshold
iou_thres=0.45  # NMS IOU threshold
max_det=1000  # maximum detections per image
hide_labels = False
hide_conf = False
line_thickness = 3
model = DetectMultiBackend("./helmet_head_person_s.pt")
stride, names, pt = model.stride, model.names, model.pt
imgsz=(640, 640)
model.warmup(imgsz=(1 if pt or model.triton else 1, 3, *imgsz))  # warmup
img_loader = LoadImages('./test_data')
save_dir = "./detect_imgs/"
# Run inference
for path, im, im0s in img_loader:
    im = torch.from_numpy(im)
    im = im.half() if model.fp16 else im.float()  # uint8 to fp16/32
    im /= 255  # 0 - 255 to 0.0 - 1.0
    if len(im.shape) == 3:
        im = im[None]  # expand for batch dim
    pred = model(im)[0]

    # Apply NMS
    pred = non_max_suppression(pred, conf_thres, iou_thres, classes=None, agnostic=False, max_det=max_det)
    # Process detections
    for i, det in enumerate(pred):  # per image
        annotator = Annotator(im0s, line_width=line_thickness, example=str(names))
        if len(det):
            number_of_person = np.sum(np.array(det[:, 5] == 0.0))
            number_of_helmet = np.sum(np.array(det[:, 5] == 2.0))
            if number_of_helmet < number_of_person:
                print("Notice! Someone is not wearing a helmet!")
            no_helmet_person = find_person_without_helmet(det)
            # Rescale boxes from img_size to im0 size
            det[:, :4] = scale_boxes(im.shape[2:], det[:, :4], im0s.shape).round()
            # Print results
            for *xyxy, conf, cls in reversed(det):
                c = int(cls)  # integer class
                if tuple(xyxy) in no_helmet_person:
                    label = "No helmet, Dangerous!"
                    annotator.box_label(xyxy, label, color=(0, 0, 255))
                else:
                    label = None if hide_labels else (names[c] if hide_conf else f'{names[c]} {conf:.2f}')
                    annotator.box_label(xyxy, label, color=colors(c, True))

    # Save results (image with detections)
    print("save_path: ", save_dir + path.split("/")[-1])
    cv2.imwrite(str(save_dir + path.split("/")[-1]), im0s)

Fusing layers... 
YOLOv5s summary: 232 layers, 7251912 parameters, 0 gradients


save_path:  ./detect_imgs/000012.jpg
Notice! Someone is not wearing a helmet!
save_path:  ./detect_imgs/000178.jpg

查看目标检测后的结果:

file

如此一来,便能一站式获取图像中所有人物的检测结果以及对应的安全帽佩戴状态。还可拓展代码,将检测结果可视化,标注在原始图像上,或是接入实时视频流,实现不间断监控,让安全生产管理更智能、高效。
恭喜你!至此,你已成功完成基于 AI 图像处理的安全帽检测全部实验流程,希望你能熟练掌握这套技术方案,为更多安全生产场景赋能。

3.6 软件依赖

本实验的依赖软件版本信息如下:

  1. Python:为了方便开发者进行学习,本课程采用Python代码实现,您可以在服务器上安装一个Conda,用于创建Python环境,本实验使用的是 python 3.10
  2. ultralytics:AI视觉模型三方库,提供了多种CV模型的调用接口,内置了模型的前后处理,方便用户调用模型,本实验使用的是 8.3.48 版本;
  3. opencv-python:opencv-python 是 OpenCV 库的 Python 接口,它提供了对 OpenCV 功能的访问,包括图像处理、视频分析、计算机视觉算法和实时图像处理等,使得开发者能够在 Python 环境中轻松实现复杂的视觉任务,本实验使用的是 4.10.0.84 版本;
  4. numpy: 开源的Python科学计算库,用于进行大规模数值和矩阵运算,本实验使用的是 1.26.4 版本;
  5. torch:AI计算框架,本实验使用的是 2.1.0 版本;
  6. torch_npu:torch在昇腾硬件上的适配插件,可以让torch在昇腾硬件上执行,本实验使用的是 2.1.0.post6 版本;
  7. torchvision:提供了处理图像和视频的常用工具和预训练模型,本实验使用的是 0.16.0 版本;
  8. CANN(Compute Architecture for Neural Networks):Ascend芯片的使能软件,本实验使用的是 8.0.rc2 版本

04 课后测试

如果把输入数据换成视频,如何处理?
(提示:参考昇腾AI行业案例(一):基于AI图像处理的疲劳驾驶检测

本文由博客一文多发平台 OpenWrite 发布!

©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 217,907评论 6 506
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 92,987评论 3 395
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 164,298评论 0 354
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 58,586评论 1 293
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 67,633评论 6 392
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 51,488评论 1 302
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 40,275评论 3 418
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 39,176评论 0 276
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 45,619评论 1 314
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 37,819评论 3 336
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 39,932评论 1 348
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 35,655评论 5 346
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 41,265评论 3 329
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 31,871评论 0 22
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 32,994评论 1 269
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 48,095评论 3 370
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 44,884评论 2 354

推荐阅读更多精彩内容