烟雾和火灾检测从零开始使用YOLOv5+PyQt5+OpenCV实现(支持图片、视频、摄像头实时检测)

这篇具有很好参考价值的文章主要介绍了烟雾和火灾检测从零开始使用YOLOv5+PyQt5+OpenCV实现(支持图片、视频、摄像头实时检测)。希望对大家有所帮助。如果存在错误或未考虑完全的地方,请大家不吝赐教,您也可以点击"举报违法"按钮提交疑问。

烟雾和火灾检测从零开始使用YOLOv5+PyQt5+OpenCV实现

全流程教程,从数据采集到模型使用到最终展示。若有任何疑问和建议欢迎评论区讨论。

先放上最终实现效果

图片检测效果
烟雾和火灾检测从零开始使用YOLOv5+PyQt5+OpenCV实现(支持图片、视频、摄像头实时检测)
视频检测效果
烟雾和火灾检测从零开始使用YOLOv5+PyQt5+OpenCV实现(支持图片、视频、摄像头实时检测)文章来源地址https://www.toymoban.com/news/detail-502977.html

烟雾和火灾检测从零开始使用YOLOv5+PyQt5+OpenCV实现(支持图片、视频、摄像头实时检测)
针对住宅、加油站、公路、森林等火灾高发场景,可以自动检测监控区域内的烟雾和火灾,帮助相关人员及时应对,最大程度降低人员伤亡及财物损失,模型效果如图所示。

1. 数据集的制作

已有一份数据且形成了对应的数据集。下载链接为数据集。数据集中包含了5000张已经标注好的数据。该项目采用目标检测的标注方式,提供了VOC数据集格式。

示例图片
烟雾和火灾检测从零开始使用YOLOv5+PyQt5+OpenCV实现(支持图片、视频、摄像头实时检测)

数据集图片格式是VOC数据格式,VOC数据是每个图像文件对应一个同名的xml文件,xml文件内包含对应图片的基本信息,比如文件名、来源、图像尺寸以及图像中包含的物体区域信息和类别信息等。

xml文件中包含以下字段:

filename,表示图像名称。

size,表示图像尺寸。包括:图像宽度、图像高度、图像深度。

<size>
   <width>500</width>
   <height>375</height>
   <depth>3</depth>
</size>

最终数据集文件组织结构为:


 ├── dataset
	├── annotations
  │   ├── fire_000001.xml
  │   ├── fire_000002.xml
  │   ├── fire_000003.xml
  │   |   ...
  ├── images
  │   ├── fire_000001.jpg
  │   ├── fire_000003.jpg
  │   ├── fire_000003.jpg
  │   |   ...
  ├── label_list.txt
  ├── train.txt
  └── valid.txt

自己制作数据集可以参考如下步骤:

1.1 数据集采集

可以通过爬虫爬取一些图片。

1.2 使用labelme对图片进行标注

labelme是图形图像注释工具,它是用Python编写的,并将Qt用于其图形界面。说直白点,它是有界面的, 像软件一样,可以交互,但是它又是由命令行启动的,比软件的使用稍微麻烦点。其界面如下图:

烟雾和火灾检测从零开始使用YOLOv5+PyQt5+OpenCV实现(支持图片、视频、摄像头实时检测)
github链接: labelme https://github.com/wkentaro/labelme
它的功能很多,包括:

  • 对图像进行多边形,矩形,圆形,多段线,线段,点形式的标注(可用于目标检-测,图像分割等任务)。
  • 对图像进行进行 flag形式的标注(可用于图像分类 和 清理 任务)。
  • 视频标注 - 生成 VOC 格式的数据集(for semantic / instancesegmentation)
  • 生成 COCO 格式的数据集(for instance segmentation)

2. YOLOv5

2.1YOLO算法简单介绍

YOLO框架(You Only Look Once)与RCNN系列算法不一样,是以不同的方式处理对象检测。它将整个图像放在一个实例中,并预测这些框的边界框坐标和及所属类别概率。使用YOLO算法最大优的点是速度极快,每秒可处理45帧,也能够理解一般的对象表示。

在本节中,将介绍YOLO用于检测给定图像中的对象的处理步骤。

首先,输入图像:
烟雾和火灾检测从零开始使用YOLOv5+PyQt5+OpenCV实现(支持图片、视频、摄像头实时检测)

然后,YOLO将输入图像划分为网格形式(例如3 X 3):
烟雾和火灾检测从零开始使用YOLOv5+PyQt5+OpenCV实现(支持图片、视频、摄像头实时检测)

最后,对每个网格应用图像分类和定位处理,获得预测对象的边界框及其对应的类概率。
整个过程是不是很清晰,下面逐一详细介绍。首先需要将标记数据传递给模型以进行训练。假设已将图像划分为大小为3 X 3的网格,且总共只有3个类别,分别是行人(c1)、汽车(c2)和摩托车(c3)。因此,对于每个单元格,标签y将是一个八维向量:

烟雾和火灾检测从零开始使用YOLOv5+PyQt5+OpenCV实现(支持图片、视频、摄像头实时检测)

其中:
pc定义对象是否存在于网格中(存在的概率);
bx、by、bh、bw指定边界框;
c1、c2、c3代表类别。如果检测对象是汽车,则c2位置处的值将为1,c1和c3处的值将为0;
假设从上面的例子中选择第一个网格:

烟雾和火灾检测从零开始使用YOLOv5+PyQt5+OpenCV实现(支持图片、视频、摄像头实时检测)

由于此网格中没有对象,因此pc将为零,此网格的y标签将为:
烟雾和火灾检测从零开始使用YOLOv5+PyQt5+OpenCV实现(支持图片、视频、摄像头实时检测)

?意味着其它值是什么并不重要,因为网格中没有对象。下面举例另一个有车的网格(c2=1):
烟雾和火灾检测从零开始使用YOLOv5+PyQt5+OpenCV实现(支持图片、视频、摄像头实时检测)

在为此网格编写y标签之前,首先要了解YOLO如何确定网格中是否存在实际对象。大图中有两个物体(两辆车),因此YOLO将取这两个物体的中心点,物体将被分配到包含这些物体中心的网格中。中心点左侧网格的y标签会是这样的:
烟雾和火灾检测从零开始使用YOLOv5+PyQt5+OpenCV实现(支持图片、视频、摄像头实时检测)

由于此网格中存在对象,因此pc将等于1,bx、by、bh、bw将相对于正在处理的特定网格单元计算。由于检测出的对象是汽车,所以c2=1,c1和c3均为0。对于9个网格中的每一个单元格,都具有八维输出向量。最终的输出形状为3X3X8。
使用上面的例子(输入图像:100X100X3,输出:3X3X8),模型将按如下方式进行训练:
烟雾和火灾检测从零开始使用YOLOv5+PyQt5+OpenCV实现(支持图片、视频、摄像头实时检测)

使用经典的CNN网络构建模型,并进行模型训练。在测试阶段,将图像传递给模型,经过一次前向传播就得到输出y。为了简单起见,使用3X3网格解释这一点,但通常在实际场景中会采用更大的网格(比如19X19)。

即使一个对象跨越多个网格,它也只会被分配到其中点所在的单个网格。可以通过增加更多网格来减少多个对象出现在同一网格单元中的几率。

2.2 YOLOv5获取与调试

2.2.1 下载yolov5代码

如果你有git,则使用git clone

git clone https://github.com/ultralytics/yolov5  # clone 

如果你没有git,你可以使用Dwonload ZIP下载代码项目。
yolov5代码地址:yolov5
注意:yolov5的代码是最新的v8.0版本
可以通过这个链接下载6.0版本https://github.com/ultralytics/yolov5/tree/v6.0

2.2.2 安装yolov5训练所需的第三方库:
  1. 检查是否正确安装好anaconda。
    windows+r打开cmd,输入 conda -V。若出现版本号,则安装成功。
    烟雾和火灾检测从零开始使用YOLOv5+PyQt5+OpenCV实现(支持图片、视频、摄像头实时检测)
  2. 检查是否正确安装好pytorch
import torch
if __name__ == '__main__':
     print(torch.zeros(1))

烟雾和火灾检测从零开始使用YOLOv5+PyQt5+OpenCV实现(支持图片、视频、摄像头实时检测)

  1. 进入yolov5文件夹目录安装第三方库
    cd [path_to_yolov5]
    如下图所示
    烟雾和火灾检测从零开始使用YOLOv5+PyQt5+OpenCV实现(支持图片、视频、摄像头实时检测)
    安装第三方库
pip install -r requirement.txt 

如下图所示,等待安装完成
烟雾和火灾检测从零开始使用YOLOv5+PyQt5+OpenCV实现(支持图片、视频、摄像头实时检测)

2.2.3 下载预训练的权重文件

我们需要下载其预训练的权重文件然后再此基础上进行调整训练,这样在数据集数量较小时也能取得不错的检测准确率。

供选择的有yolov5s,yolov5m,yolov5l,yolov5x。模型的大小逐渐增大,训练时间更长,准确率提高。
烟雾和火灾检测从零开始使用YOLOv5+PyQt5+OpenCV实现(支持图片、视频、摄像头实时检测)

这里我们以yolov5s为例训练。下载地址为yolov5s.pt
所有权重下载地址可在https://github.com/ultralytics/yolov5/releases/tag/v6.0界面找到

2.2.4 配置自己的yaml文件

配置models/yolov5s_my.yaml 可以直接复制yolov5s.yaml文件,然后在nc即类别出进行修改,对于本文检测数量为2,表示起火和烟雾。其中anchors参数表示锚框的大小,可以通过对数据集进行knn聚类得到,这里直接使用默认即对COCO数据集进行聚类的结果。

# YOLOv5 🚀 by Ultralytics, GPL-3.0 license

# Parameters
nc: 2 # number of classes
depth_multiple: 0.33  # model depth multiple
width_multiple: 0.50  # layer channel multiple
anchors:
  - [10,13, 16,30, 33,23]  # P3/8
  - [30,61, 62,45, 59,119]  # P4/16
  - [116,90, 156,198, 373,326]  # P5/32

# YOLOv5 v6.0 backbone
backbone:
  # [from, number, module, args]
  [[-1, 1, Conv, [64, 6, 2, 2]],  # 0-P1/2
   [-1, 1, Conv, [128, 3, 2]],  # 1-P2/4
   [-1, 3, C3, [128]],
   [-1, 1, Conv, [256, 3, 2]],  # 3-P3/8
   [-1, 6, C3, [256]],
   [-1, 1, Conv, [512, 3, 2]],  # 5-P4/16
   [-1, 9, C3, [512]],
   [-1, 1, Conv, [1024, 3, 2]],  # 7-P5/32
   [-1, 3, C3, [1024]],
   [-1, 1, SPPF, [1024, 5]],  # 9
  ]

# YOLOv5 v6.0 head
head:
  [[-1, 1, Conv, [512, 1, 1]],
   [-1, 1, nn.Upsample, [None, 2, 'nearest']],
   [[-1, 6], 1, Concat, [1]],  # cat backbone P4
   [-1, 3, C3, [512, False]],  # 13

   [-1, 1, Conv, [256, 1, 1]],
   [-1, 1, nn.Upsample, [None, 2, 'nearest']],
   [[-1, 4], 1, Concat, [1]],  # cat backbone P3
   [-1, 3, C3, [256, False]],  # 17 (P3/8-small)

   [-1, 1, Conv, [256, 3, 2]],
   [[-1, 14], 1, Concat, [1]],  # cat head P4
   [-1, 3, C3, [512, False]],  # 20 (P4/16-medium)

   [-1, 1, Conv, [512, 3, 2]],
   [[-1, 10], 1, Concat, [1]],  # cat head P5
   [-1, 3, C3, [1024, False]],  # 23 (P5/32-large)

   [[17, 20, 23], 1, Detect, [nc, anchors]],  # Detect(P3, P4, P5)
  ]

烟雾和火灾检测从零开始使用YOLOv5+PyQt5+OpenCV实现(支持图片、视频、摄像头实时检测)

配置my.yaml 。这里train指定训练数据集所在位置,val测试数据集所在位置,nc类别数,names类别的名称(注意顺序)

train: D:\\BaiduNetdiskDownload\\train
val: D:\\BaiduNetdiskDownload\\val
nc: 2
names: ['fire','smoke']

烟雾和火灾检测从零开始使用YOLOv5+PyQt5+OpenCV实现(支持图片、视频、摄像头实时检测)

2.2.5 开始训练
python3 train.py --img 640 --batch 8 --epochs 50 --data my.yaml --cfg yolov5s_my.yaml --weights "yolov5s.pt"

烟雾和火灾检测从零开始使用YOLOv5+PyQt5+OpenCV实现(支持图片、视频、摄像头实时检测)
如果出现显卡空间不足的情况可以改小–bath参数
烟雾和火灾检测从零开始使用YOLOv5+PyQt5+OpenCV实现(支持图片、视频、摄像头实时检测)

会在/runs/train/exp/weights/best.pt下生成最终的权重文件。
烟雾和火灾检测从零开始使用YOLOv5+PyQt5+OpenCV实现(支持图片、视频、摄像头实时检测)
将此文件复制到yolov5目录下后续使用
烟雾和火灾检测从零开始使用YOLOv5+PyQt5+OpenCV实现(支持图片、视频、摄像头实时检测)

2.2.5 编写detection方法用于后续检测的调用

后续进行图片或者视频检测时,只需要传入YOLOv5模型和图片,便会返回检测后图片

import os
import sys
from pathlib import Path

import numpy as np
import torch


FILE = Path(__file__).resolve()
ROOT = FILE.parents[0]  # YOLOv5 root directory
if str(ROOT) not in sys.path:
    sys.path.append(str(ROOT))  # add ROOT to PATH
ROOT = Path(os.path.relpath(ROOT, Path.cwd()))  # relative

from models.experimental import attempt_load
from utils.general import apply_classifier, check_img_size, check_imshow, check_requirements, check_suffix, colorstr, \
    increment_path, non_max_suppression, print_args, save_one_box, scale_coords, strip_optimizer, xyxy2xywh, LOGGER
from utils.plots import Annotator, colors
from utils.torch_utils import load_classifier, select_device, time_sync
from utils.augmentations import Albumentations, augment_hsv, copy_paste, letterbox, mixup, random_perspective

@torch.no_grad()
def detection(model,input_img):
    imgsz = 640  # inference size (pixels)
    conf_thres = 0.25   # confidence threshold
    iou_thres = 0.45   # NMS IOU threshold
    max_det = 1000  # maximum detections per image
    device = '0'  # cuda device, i.e. 0 or 0,1,2,3 or cpu
    view_img = False   # show results
    save_txt = False  # save results to *.txt
    save_conf = False    # save confidences in --save-txt labels
    save_crop = False   # save cropped prediction boxes
    nosave = False  # do not save images/videos
    classes = None   # filter by class: --class 0, or --class 0 2 3
    agnostic_nms = False   # class-agnostic NMS
    augment = False   # augmented inference
    project = ROOT / 'runs/detect',  # save results to project/name
    name = 'exp'   # save results to project/name
    exist_ok = False,  # existing project/name ok, do not increment
    line_thickness = 3   # bounding box thickness (pixels)
    hide_labels = False   # hide labels
    hide_conf = False   # hide confidences
    half = False  # use FP16 half-precision inference
    # Directories
    # Initialize
    device = select_device(device)
    weights = 'best.pt'
    # # Load model
    w = str(weights[0] if isinstance(weights, list) else weights)
    classify, suffix, suffixes = False, Path(w).suffix.lower(), ['.pt', '.onnx', '.tflite', '.pb', '']
    check_suffix(w, suffixes)  # check weights have acceptable suffix
    pt, onnx, tflite, pb, saved_model = (suffix == x for x in suffixes)  # backend booleans
    stride = int(model.stride.max())
    names = model.module.names if hasattr(model, 'module') else model.names  # get class names
    imgsz = check_img_size(imgsz, s=stride)  # check image size
    img0 = input_img  # BGR
    im0s=img0
    # Padded resize
    img = letterbox(img0, imgsz, stride=32, auto=pt)[0]

    # Convert
    img = img.transpose((2, 0, 1))[::-1]  # HWC to CHW, BGR to RGB
    img = np.ascontiguousarray(img)
    bs = 1  # batch_size

    dt, seen = [0.0, 0.0, 0.0], 0
    t1 = time_sync()
    img = torch.from_numpy(img).to(device)
    img = img.float()  # uint8 to fp16/32
    img /= 255.0  # 0 - 255 to 0.0 - 1.0
    if len(img.shape) == 3:
        img = img[None]  # expand for batch dim
    t2 = time_sync()
    dt[0] += t2 - t1
    # Inference
    if pt:
        pred = model(img, augment=augment)[0]
    t3 = time_sync()
    dt[1] += t3 - t2

    # NMS
    pred = non_max_suppression(pred, conf_thres, iou_thres, classes, agnostic_nms, max_det=max_det)
    dt[2] += time_sync() - t3
    # Process predictions
    for i, det in enumerate(pred):  # per image
        seen += 1
        im0=im0s.copy()
        annotator = Annotator(im0, line_width=line_thickness, example=str(names))
        if len(det):
            # Rescale boxes from img_size to im0 size
            det[:, :4] = scale_coords(img.shape[2:], det[:, :4], im0.shape).round()
            # Write results
            for *xyxy, conf, cls in reversed(det):
                  # Add bbox to image
                c = int(cls)  # integer class
                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))
        # Stream results
        im0 = annotator.result()
        return im0

3. Pyqt5

我们通过pyqt来制作展示的界面

3.1介绍

PyQt5 是 Digia的一套 Qt5 应用框架与 python 的结合,同时支持 python2.x和 python3.x。

这里使用的是Python 3.x。Qt库由 Riverbank Computing开发,是最强大的GUI库之一 。

PyQt5 是由一系列 Python 模块组成。超过 620 个类,6000 函数和方法。能在诸如 Unix、Windows 和Mac OS 等主流操作系统上运行。PyQt5 有两种证书,GPL和 商业证书。

3.2 window平台安装

PyQt5 有两种安装方式,一种是从官网下载源码安装,另外一种是使用 pip 安装。

这里我推荐大家使用pip 安装。因为它会自动根据你的Python 版本来选择合适的 PyQt5 版本,如果是手动下载源码安装,难免会选择出错。建议使用比较稳妥的安装方式。

pip3 install PyQt5 

另外,如果你的网络访问外网不是很好的话建议使用豆瓣的镜像下载,不然会很很慢或者直接安装失败。

pip install PyQt5 -i https://pypi.douban.com/simple

执行以下代码:

import sys
from PyQt5.QtWidgets import QWidget, QApplication

app = QApplication(sys.argv)
widget = QWidget()
widget.resize(640, 480)
widget.setWindowTitle("Hello, PyQt5!")
widget.show()
sys.exit(app.exec())

如果没有报错,弹出了一个标题为"Hello, PyQt5!"的窗口,则说明安装成功。
烟雾和火灾检测从零开始使用YOLOv5+PyQt5+OpenCV实现(支持图片、视频、摄像头实时检测)
若pip安装pyqt5报错 error: Microsoft Visual C++ 14.0 or greater is required. Get it with “Microsoft C++ Build Tools”: https://visualstudio.microsoft.com/visual-cpp-build-tools/

error: Microsoft Visual C++ 14.0 or greater is required. Get it with "Microsoft C++ Build Tools": https://visualstudio.microsoft.com/visual-cpp-build-tools/

可参考我另外一篇文章 已解决(pip安装pyqt5报错) error: Microsoft Visual C++ 14.0 or greater is required. Get it with “Microsoft
对于运行项目做到此即可,若对pyqt感兴趣如图形界面开发工具Qt Designer等可以参考文章https://zhuanlan.zhihu.com/p/162866700

4. OpenCV

OpenCV(开源的计算机视觉库)是基于BSD协议,因此它可免费用于学术和商业用途。其提供C++,C,Python和Java接口,支持Windows,Linux,Mac OS,iOS和Android。

我们使用OpenCV来处理图片和视频,以便于将图片转为Yolov5模型需要的输入。

安装

首先我们得先安装另一个第三方库numpy,这是opencv的依赖库,没有它无法进行python-opencv开发。
安装numpy:pip install numpy
安装opencv-python: pip install opencv-python

5. 界面布局

使用pyqt设计我们界面的布局,新建布局代码mainwindow_ui .py。pyqt原始提供的组件并不美观,为了美化组件可以使用qt_material。其安装和使用都比较简单。
安装

pip install qt_material
# -*- coding: utf-8 -*-

# Form implementation generated from reading ui file 'mainwindow_ui.ui'
#
# Created by: PyQt5 UI code generator 5.15.7
#
# WARNING: Any manual changes made to this file will be lost when pyuic5 is
# run again.  Do not edit this file unless you know what you are doing.


from PyQt5 import QtCore, QtGui, QtWidgets


class Ui_MainWindow(object):
    def setupUi(self, MainWindow):
        MainWindow.setObjectName("目标检测")
        MainWindow.resize(1367, 850)
        font = QtGui.QFont()
        font.setFamily("Adobe 宋体 Std L")
        MainWindow.setFont(font)
        self.centralwidget = QtWidgets.QWidget(MainWindow)
        self.centralwidget.setObjectName("centralwidget")
        self.groupBox = QtWidgets.QGroupBox(self.centralwidget)
        self.groupBox.setGeometry(QtCore.QRect(10, 50, 141, 771))
        font = QtGui.QFont()
        font.setFamily("Adobe 黑体 Std R")
        self.groupBox.setFont(font)
        self.groupBox.setObjectName("groupBox")
        self.btn_loadweight = QtWidgets.QPushButton(self.groupBox)
        self.btn_loadweight.setGeometry(QtCore.QRect(10, 20, 121, 31))
        self.btn_loadweight.setObjectName("btn_loadweight")
        self.btn_loadimg = QtWidgets.QPushButton(self.groupBox)
        self.btn_loadimg.setGeometry(QtCore.QRect(10, 60, 121, 31))
        self.btn_loadimg.setObjectName("btn_loadimg")
        self.btn_loadvideo = QtWidgets.QPushButton(self.groupBox)
        self.btn_loadvideo.setGeometry(QtCore.QRect(10, 100, 121, 31))
        self.btn_loadvideo.setObjectName("btn_loadvideo")
        self.btn_opencamera = QtWidgets.QPushButton(self.groupBox)
        self.btn_opencamera.setGeometry(QtCore.QRect(10, 140, 121, 31))
        self.btn_opencamera.setObjectName("btn_opencamera")
        self.btn_stop = QtWidgets.QPushButton(self.groupBox)
        self.btn_stop.setGeometry(QtCore.QRect(10, 500, 121, 31))
        self.btn_stop.setObjectName("btn_going")
        self.btn_over = QtWidgets.QPushButton(self.groupBox)
        self.btn_over.setGeometry(QtCore.QRect(10, 540, 121, 31))
        self.btn_over.setObjectName("btn_stop")
        self.fps_label = QtWidgets.QLabel(self.groupBox)
        self.fps_label.setGeometry(QtCore.QRect(50, 630, 121, 31))
        self.fps_label.setObjectName("fps_label")
        font.setPointSize(15)
        # font.setFamily("Adobe Devanagari")
        self.fps_label.setFont(font)
        self.btn_takephoto = QtWidgets.QPushButton(self.groupBox)
        self.btn_takephoto.setGeometry(QtCore.QRect(10, 380, 121, 31))
        self.btn_takephoto.setObjectName("btn_takephoto")
        self.btn_clear = QtWidgets.QPushButton(self.groupBox)
        self.btn_clear.setGeometry(QtCore.QRect(10, 460, 121, 31))
        self.btn_clear.setObjectName("btn_clear")
        self.btn_labelimg = QtWidgets.QPushButton(self.groupBox)
        self.btn_labelimg.setGeometry(QtCore.QRect(10, 420, 121, 31))
        self.btn_labelimg.setObjectName("btn_labelimg")
        self.btn_closecamera = QtWidgets.QPushButton(self.groupBox)
        self.btn_closecamera.setGeometry(QtCore.QRect(10, 220, 121, 31))
        self.btn_closecamera.setObjectName("btn_closecamera")
        self.layoutWidget = QtWidgets.QWidget(self.groupBox)
        self.layoutWidget.setGeometry(QtCore.QRect(10, 300, 121, 23))
        self.layoutWidget.setObjectName("layoutWidget")
        self.horizontalLayout_2 = QtWidgets.QHBoxLayout(self.layoutWidget)
        self.horizontalLayout_2.setContentsMargins(0, 0, 0, 0)
        self.horizontalLayout_2.setObjectName("horizontalLayout_2")
        # self.label_2 = QtWidgets.QLabel(self.layoutWidget)
        # self.label_2.setObjectName("label_2")
        # self.horizontalLayout_2.addWidget(self.label_2)
        # self.confSpinBox = QtWidgets.QDoubleSpinBox(self.layoutWidget)
        # self.confSpinBox.setObjectName("confSpinBox")
        # self.horizontalLayout_2.addWidget(self.confSpinBox)
        self.layoutWidget_2 = QtWidgets.QWidget(self.groupBox)
        self.layoutWidget_2.setGeometry(QtCore.QRect(10, 340, 121, 23))
        self.layoutWidget_2.setObjectName("layoutWidget_2")
        self.horizontalLayout_3 = QtWidgets.QHBoxLayout(self.layoutWidget_2)
        self.horizontalLayout_3.setContentsMargins(0, 0, 0, 0)
        self.horizontalLayout_3.setObjectName("horizontalLayout_3")
        # self.label_3 = QtWidgets.QLabel(self.layoutWidget_2)
        # self.label_3.setObjectName("label_3")
        # self.horizontalLayout_3.addWidget(self.label_3)
        # self.latencySpinBox = QtWidgets.QDoubleSpinBox(self.layoutWidget_2)
        # self.latencySpinBox.setObjectName("latencySpinBox")
        # self.horizontalLayout_3.addWidget(self.latencySpinBox)
        self.layoutWidget1 = QtWidgets.QWidget(self.groupBox)
        self.layoutWidget1.setGeometry(QtCore.QRect(10, 260, 121, 23))
        self.layoutWidget1.setObjectName("layoutWidget1")
        self.horizontalLayout = QtWidgets.QHBoxLayout(self.layoutWidget1)
        self.horizontalLayout.setContentsMargins(0, 0, 0, 0)
        self.horizontalLayout.setObjectName("horizontalLayout")
        # self.label = QtWidgets.QLabel(self.layoutWidget1)
        # self.label.setObjectName("label")
        # self.horizontalLayout.addWidget(self.label)
        # self.iouSpinBox = QtWidgets.QDoubleSpinBox(self.layoutWidget1)
        # self.iouSpinBox.setObjectName("iouSpinBox")
        # self.horizontalLayout.addWidget(self.iouSpinBox)
        self.btn_camera_detect = QtWidgets.QPushButton(self.groupBox)
        self.btn_camera_detect.setGeometry(QtCore.QRect(10, 180, 121, 31))
        self.btn_camera_detect.setObjectName("btn_camera_detect")
        self.groupBox_2 = QtWidgets.QGroupBox(self.centralwidget)
        self.groupBox_2.setGeometry(QtCore.QRect(170, 50, 1181, 591))
        self.groupBox_2.setObjectName("groupBox_2")
        self.label_origin = QtWidgets.QLabel(self.groupBox_2)
        self.label_origin.setGeometry(QtCore.QRect(10, 30, 571, 551))
        font = QtGui.QFont()
        font.setPointSize(15)
        self.label_origin.setFont(font)
        self.label_origin.setStyleSheet("background-color: rgb(85, 170, 255);")
        self.label_origin.setAlignment(QtCore.Qt.AlignCenter)
        self.label_origin.setObjectName("label_origin")
        self.label_detect = QtWidgets.QLabel(self.groupBox_2)
        self.label_detect.setGeometry(QtCore.QRect(600, 30, 571, 551))
        font = QtGui.QFont()
        font.setPointSize(15)
        self.label_detect.setFont(font)
        self.label_detect.setStyleSheet("background-color: rgb(170, 255, 127);")
        self.label_detect.setAlignment(QtCore.Qt.AlignCenter)
        self.label_detect.setObjectName("label_detect")
        self.label_mian_titlle = QtWidgets.QLabel(self.centralwidget)
        self.label_mian_titlle.setGeometry(QtCore.QRect(530, 10, 281, 41))
        font = QtGui.QFont()
        font.setFamily("Adobe Devanagari")
        font.setPointSize(20)
        self.label_mian_titlle.setFont(font)
        self.label_mian_titlle.setLayoutDirection(QtCore.Qt.LeftToRight)
        self.label_mian_titlle.setAlignment(QtCore.Qt.AlignCenter)
        self.label_mian_titlle.setObjectName("label_mian_titlle")
        self.lcdNumber = QtWidgets.QLCDNumber(self.centralwidget)
        self.lcdNumber.setGeometry(QtCore.QRect(1050, 20, 301, 31))
        font = QtGui.QFont()
        font.setFamily("Adobe 黑体 Std R")
        self.lcdNumber.setFont(font)
        self.lcdNumber.setObjectName("lcdNumber")
        self.lcdNumber.setDigitCount(20)
        self.groupBox_3 = QtWidgets.QGroupBox(self.centralwidget)
        self.groupBox_3.setGeometry(QtCore.QRect(170, 650, 581, 171))
        self.groupBox_3.setObjectName("groupBox_3")
        self.textBrowser_print = QtWidgets.QTextBrowser(self.groupBox_3)
        self.textBrowser_print.setGeometry(QtCore.QRect(10, 30, 561, 141))
        self.textBrowser_print.setObjectName("textBrowser_print")
        self.groupBox_4 = QtWidgets.QGroupBox(self.centralwidget)
        self.groupBox_4.setGeometry(QtCore.QRect(770, 650, 581, 171))
        self.groupBox_4.setObjectName("groupBox_4")
        self.textBrowser_detect = QtWidgets.QTextBrowser(self.groupBox_4)
        self.textBrowser_detect.setGeometry(QtCore.QRect(10, 30, 561, 141))
        self.textBrowser_detect.setObjectName("textBrowser_detect")
        self.label_author = QtWidgets.QLabel(self.centralwidget)
        self.label_author.setGeometry(QtCore.QRect(20, 20, 111, 16))
        font = QtGui.QFont()
        font.setFamily("Adobe 黑体 Std R")
        font.setPointSize(12)
        self.label_author.setFont(font)
        self.label_author.setObjectName("label_author")
        MainWindow.setCentralWidget(self.centralwidget)
        self.menubar = QtWidgets.QMenuBar(MainWindow)
        self.menubar.setGeometry(QtCore.QRect(0, 0, 1367, 23))
        self.menubar.setObjectName("menubar")
        MainWindow.setMenuBar(self.menubar)
        self.statusbar = QtWidgets.QStatusBar(MainWindow)
        self.statusbar.setObjectName("statusbar")
        MainWindow.setStatusBar(self.statusbar)

        self.retranslateUi(MainWindow)
        QtCore.QMetaObject.connectSlotsByName(MainWindow)

    def retranslateUi(self, MainWindow):
        _translate = QtCore.QCoreApplication.translate
        MainWindow.setWindowTitle(_translate("MainWindow", "MainWindow"))
        self.groupBox.setTitle(_translate("MainWindow", ""))
        self.btn_loadweight.setText(_translate("MainWindow", "加载Weights"))
        self.btn_loadimg.setText(_translate("MainWindow", "图片检测"))
        self.btn_loadvideo.setText(_translate("MainWindow", "视频检测"))
        self.btn_opencamera.setText(_translate("MainWindow", "打开摄像头"))
        self.btn_over.setText(_translate("MainWindow", "结束"))
        self.fps_label.setText(_translate("MainWindow", "帧率"))
        self.btn_stop.setText(_translate("MainWindow", "暂停"))
        self.btn_takephoto.setText(_translate("MainWindow", "拍照"))
        self.btn_clear.setText(_translate("MainWindow", "清除输出"))
        self.btn_labelimg.setText(_translate("MainWindow", "标注工具"))
        self.btn_closecamera.setText(_translate("MainWindow", "关闭摄像头"))
        # self.label_2.setText(_translate("MainWindow", "conf:"))
        # self.label_3.setText(_translate("MainWindow", "latency:"))
        # self.label.setText(_translate("MainWindow", "IoU:"))
        self.btn_camera_detect.setText(_translate("MainWindow", "摄像头检测"))
        self.groupBox_2.setTitle(_translate("MainWindow", "显示区域"))
        self.label_origin.setText(_translate("MainWindow", "原图区域"))
        self.label_detect.setText(_translate("MainWindow", "检测区域"))
        self.label_mian_titlle.setText(_translate("MainWindow", "YoLo检测界面"))
        self.groupBox_3.setTitle(_translate("MainWindow", "打印输出"))
        self.groupBox_4.setTitle(_translate("MainWindow", "检测输出"))
        self.label_author.setText(_translate("MainWindow", " "))

6. 图片、视频、摄像头实时三个模块整合完整代码

注意这个代码要放在yolov5项目代码中一起使用以及上一节的布局代码mainwindow_ui.py。yolov5代码的获取和配置在第2节YOLOV5有描述。yolov5代码获取链接yolo。单独运行这个是不行的。

import argparse
import datetime
import os
import random
import sys
import time

import cv2
import numpy as np
import torch
from PyQt5 import QtWidgets, QtGui, QtCore
from PyQt5.QtCore import QTimer, QDateTime, QDate, QTime, QThread, pyqtSignal
from PyQt5.QtGui import *
from PyQt5.QtWidgets import *
from torch.backends import cudnn

from models.experimental import attempt_load
from  mainwindow_ui import Ui_MainWindow
# from utils.datasets import letterbox
from utils.general import check_img_size, non_max_suppression, scale_coords
# from utils.plots import plot_one_box2

def letterbox(img, new_shape=(640, 640), color=(114, 114, 114), auto=True, scaleFill=False, scaleup=True, stride=32):
    # Resize and pad image while meeting stride-multiple constraints
    shape = img.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 test 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
        img = cv2.resize(img, 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))
    img = cv2.copyMakeBorder(img, top, bottom, left, right, cv2.BORDER_CONSTANT, value=color)  # add border
    return img, ratio, (dw, dh)
def plot_one_box2(x, img, color=None, label=None, line_thickness=3):
    # Plots one bounding box on image img
    tl = line_thickness or round(0.002 * (img.shape[0] + img.shape[1]) / 2) + 1  # line/font thickness
    color = color or [random.randint(0, 255) for _ in range(3)]
    c1, c2 = (int(x[0]), int(x[1])), (int(x[2]), int(x[3]))
    cv2.rectangle(img, c1, c2, color, thickness=tl, lineType=cv2.LINE_AA)
    if label:
        tf = max(tl - 1, 1)  # font thickness
        t_size = cv2.getTextSize(label, 0, fontScale=tl / 3, thickness=tf)[0]
        c2 = c1[0] + t_size[0], c1[1] - t_size[1] - 3
        cv2.rectangle(img, c1, c2, color, -1, cv2.LINE_AA)  # filled
        cv2.putText(img, label, (c1[0], c1[1] - 2), 0, tl / 3, [225, 255, 255], thickness=tf, lineType=cv2.LINE_AA)
    single_info = "Position: (%d, %d), (%d, %d), Obj and Confidence: %s"%(c1[0], c1[1], c2[0], c2[1], label)
    return single_info
class UI_Logic_Window(QtWidgets.QMainWindow):
    def __init__(self, parent=None):
        super(UI_Logic_Window, self).__init__(parent)
        self.initUI()

    # 初始化界面
    def initUI(self):
        self.setWindowIcon(QIcon("./icon/yolo.png"))
        # 创建一个窗口对象
        self.ui = Ui_MainWindow()
        self.ui.setupUi(self)
        self.timer_video = QtCore.QTimer(self) # 创建定时器
        self.timer_photo = QtCore.QTimer(self) # 创建定时器
        self.output_folder = 'output/'
        self.cap = cv2.VideoCapture()
        self.vid_writer = None
        self.camera_detect = False
        self.num_stop = 1  # 暂停与播放辅助信号,note:通过奇偶来控制暂停与播放
        self.openfile_name_model = None        # 权重初始文件名
        self.count = 0
        self.start_time = time.time()        # 打开线程
        self.stop_going = 0



        # 刷新lcd时间
        self.lcd_time = QTimer(self)
        self.lcd_time.setInterval(1000)
        self.lcd_time.timeout.connect(self.refresh)
        self.lcd_time.start()

        self.ui.textBrowser_print.append("特别说明:如需启动检测,请先加载weights文件!!!")
        self.init_slots()

    # 刷新时间
    def refresh(self):
        now_time = datetime.datetime.now().strftime("%Y-%m-%d %H:%M:%S")
        self.ui.lcdNumber.display(now_time)

    # 初始化槽函数
    def init_slots(self):
        self.ui.btn_loadweight.clicked.connect(self.load_model)
        self.ui.btn_loadimg.clicked.connect(self.button_image_open)
        self.ui.btn_loadvideo.clicked.connect(self.button_video_open)
        self.ui.btn_opencamera.clicked.connect(self.button_camera_open)
        self.ui.btn_camera_detect.clicked.connect(self.button_camera_detect)
        self.ui.btn_stop.clicked.connect(self.button_stop)
        self.ui.btn_over.clicked.connect(self.button_over)
        self.ui.btn_closecamera.clicked.connect(self.button_closecamera)
        self.ui.btn_clear.clicked.connect(self.button_clear)
        self.ui.btn_takephoto.clicked.connect(self.button_takephoto)
        self.ui.btn_labelimg.clicked.connect(self.button_labelimg)

        self.timer_video.timeout.connect(self.show_video_frame)  # 定时器超时,将槽绑定至show_video_frame
        self.timer_photo.timeout.connect(self.show_image)  # 定时器超时,将槽绑定至show_video_frame


    # 加载模型
    def load_model(self):
        self.openfile_name_model, _ = QtWidgets.QFileDialog.getOpenFileName(self.ui.btn_loadweight, '选择weights文件',
                                                                  'weights/', "*.pt;;*.pth")
        if not self.openfile_name_model:
            QtWidgets.QMessageBox.warning(self, u"Warning", u"打开权重失败", buttons=QtWidgets.QMessageBox.Ok,
                                          defaultButton=QtWidgets.QMessageBox.Ok)
            self.ui.textBrowser_print.append("打开权重失败")
        else:
            self.ui.textBrowser_print.append("加载weights文件地址为:" + str(self.openfile_name_model))
            self.model_init() #初始化权重

    # 初始化权重
    def model_init(self):
        # 模型相关参数配置
        parser = argparse.ArgumentParser()
        parser.add_argument('--weights', nargs='+', type=str, default='weights/yolov5s6.pt', help='model.pt path(s)')
        parser.add_argument('--source', type=str, default='data/images', help='source')  # file/folder, 0 for webcam
        parser.add_argument('--img-size', type=int, default=640, help='inference size (pixels)')
        parser.add_argument('--conf-thres', type=float, default=0.25, help='object confidence threshold')
        parser.add_argument('--iou-thres', type=float, default=0.45, help='IOU threshold for NMS')
        parser.add_argument('--device', default='cuda', help='cuda device, i.e. 0 or 0,1,2,3 or cpu')
        parser.add_argument('--view-img', action='store_true', help='display results')
        parser.add_argument('--save-txt', action='store_true', help='save results to *.txt')
        parser.add_argument('--save-conf', action='store_true', help='save confidences in --save-txt labels')
        parser.add_argument('--nosave', action='store_true', help='do not save images/videos')
        parser.add_argument('--classes', nargs='+', type=int, help='filter by class: --class 0, or --class 0 2 3')
        parser.add_argument('--agnostic-nms', action='store_true', help='class-agnostic NMS')
        parser.add_argument('--augment', action='store_true', help='augmented inference')
        parser.add_argument('--update', action='store_true', help='update all models')
        parser.add_argument('--project', default='runs/detect', help='save results to project/name')
        parser.add_argument('--name', default='exp', help='save results to project/name')
        parser.add_argument('--exist-ok', action='store_true', help='existing project/name ok, do not increment')
        self.opt = parser.parse_args()
        print(self.opt)
        # 默认使用opt中的设置(权重等)来对模型进行初始化
        source, weights, view_img, save_txt, imgsz = self.opt.source, self.opt.weights, self.opt.view_img, self.opt.save_txt, self.opt.img_size
        # 改变权重文件
        if self.openfile_name_model:
            weights = self.openfile_name_model
            print(weights)

        self.device = torch.device("cuda" if torch.cuda.is_available() else "cpu")
        self.half = self.device.type != 'cpu'  # half precision only supported on CUDA

        cudnn.benchmark = True

        # Load model
        self.model = attempt_load(weights, map_location=self.device)  # load FP32 model
        stride = int(self.model.stride.max())  # model stride
        self.imgsz = check_img_size(imgsz, s=stride)  # check img_size
        if self.half:
            self.model.half()  # to FP16

        #  Second-stage classifier

        # Get names and colors
        self.names = self.model.module.names if hasattr(self.model, 'module') else self.model.names
        self.colors = [[random.randint(0, 255) for _ in range(3)] for _ in self.names]
        # 设置提示框
        QtWidgets.QMessageBox.information(self, u"Notice", u"模型加载完成", buttons=QtWidgets.QMessageBox.Ok,
                                          defaultButton=QtWidgets.QMessageBox.Ok)

        self.ui.textBrowser_print.append("模型加载完成")

    # 打开图片
    def button_image_open(self):
            # 打印信息显示在界面
            name_list = []
            try:
                img_name, _ = QtWidgets.QFileDialog.getOpenFileName(self, "打开图片", "data/images", "*.jpg;;*.png;;All Files(*)")
            except OSError as reason:
                print('文件打开出错啦!核对路径是否正确'+ str(reason))
                self.ui.textBrowser_print.append("文件打开出错啦!核对路径是否正确")
            else:
                # 判断图片是否为空
                if not img_name:
                    QtWidgets.QMessageBox.warning(self, u"Warning", u"打开图片失败", buttons=QtWidgets.QMessageBox.Ok,
                                                  defaultButton=QtWidgets.QMessageBox.Ok)
                    self.ui.textBrowser_print.append("打开图片失败")
                else:
                    self.ui.textBrowser_print.append("打开图片成功")
                    img = cv2.imread(img_name)
                    print("img_name:", img_name)
                    self.origin = cv2.cvtColor(img, cv2.COLOR_BGR2BGRA)
                    self.origin = cv2.resize(self.origin, (640, 480), interpolation=cv2.INTER_AREA)
                    self.QtImg_origin = QtGui.QImage(self.origin.data, self.origin.shape[1], self.origin.shape[0],
                                              QtGui.QImage.Format_RGB32)
                    self.ui.label_origin.setPixmap(QtGui.QPixmap.fromImage(self.QtImg_origin))
                    self.ui.label_origin.setScaledContents(True)  # 设置图像自适应界面大小

                    info_show = self.detect(name_list, img)
                    # print(info_show)
                    # 获取当前系统时间,作为img文件名
                    now = time.strftime("%Y-%m-%d-%H-%M-%S", time.localtime(time.time()))
                    file_extension = img_name.split('.')[-1]
                    new_filename = now + '.' + file_extension  # 获得文件后缀名
                    file_path = self.output_folder + 'img_output/' + new_filename
                    cv2.imwrite(file_path, img)
                    # 检测信息显示在界面
                    self.ui.textBrowser_detect.append(info_show)

                    # 检测结果显示在界面
                    self.result = cv2.cvtColor(img, cv2.COLOR_BGR2BGRA)
                    self.result = cv2.resize(self.result, (640, 480), interpolation=cv2.INTER_AREA)
                    self.QtImg = QtGui.QImage(self.result.data, self.result.shape[1], self.result.shape[0],
                                              QtGui.QImage.Format_RGB32)
                    self.ui.label_detect.setPixmap(QtGui.QPixmap.fromImage(self.QtImg))
                    self.ui.label_detect.setScaledContents(True)  # 设置图像自适应界面大小

    # 目标检测
    def detect(self, name_list, img):
        '''
           :param name_list: 文件名列表
           :param img: 待检测图片
           :return: info_show:检测输出的文字信息
        '''
        showimg = img
        with torch.no_grad():
            img = letterbox(img, new_shape=self.opt.img_size)[0]
            # Convert
            img = img[:, :, ::-1].transpose(2, 0, 1)  # BGR to RGB, to 3x416x416
            img = np.ascontiguousarray(img)
            img = torch.from_numpy(img).to(self.device)
            img = img.half() if self.half else img.float()  # uint8 to fp16/32
            img /= 255.0  # 0 - 255 to 0.0 - 1.0
            if img.ndimension() == 3:
                img = img.unsqueeze(0)
            # Inference
            pred = self.model(img, augment=self.opt.augment)[0]
            # Apply NMS
            pred = non_max_suppression(pred, self.opt.conf_thres, self.opt.iou_thres, classes=self.opt.classes,
                                       agnostic=self.opt.agnostic_nms)
            info_show = ""
            # Process detections
            for i, det in enumerate(pred):
                if det is not None and len(det):
                    # Rescale boxes from img_size to im0 size
                    det[:, :4] = scale_coords(img.shape[2:], det[:, :4], showimg.shape).round()
                    for *xyxy, conf, cls in reversed(det):
                        label = '%s %.2f' % (self.names[int(cls)], conf)
                        name_list.append(self.names[int(cls)])
                        single_info = plot_one_box2(xyxy, showimg, label=label, color=self.colors[int(cls)],
                                                    line_thickness=2)
                        info_show = info_show + single_info + "\n"
        return info_show


    # 打开视频并检测
    def button_video_open(self):
        video_name, _ = QtWidgets.QFileDialog.getOpenFileName(self, "打开视频", "data/video/", "*.mp4;;*.avi;;All Files(*)")
        flag = self.cap.open(video_name)

        # 判断摄像头是否打开
        if not flag:
            QtWidgets.QMessageBox.warning(self, u"Warning", u"打开视频失败", buttons=QtWidgets.QMessageBox.Ok,defaultButton=QtWidgets.QMessageBox.Ok)
        else:
            # -------------------------写入视频----------------------------------#
            self.ui.textBrowser_print.append("打开视频检测")
            fps, w, h, save_path = self.set_video_name_and_path()
            self.vid_writer = cv2.VideoWriter(save_path, cv2.VideoWriter_fourcc(*'mp4v'), fps, (w, h))

            self.timer_video.start(30)  # 以30ms为间隔,启动或重启定时器
            # 进行视频识别时,关闭其他按键点击功能
            self.ui.btn_loadvideo.setDisabled(True)
            self.ui.btn_loadimg.setDisabled(True)
            self.ui.btn_opencamera.setDisabled(True)

    def set_video_name_and_path(self):
        # 获取当前系统时间,作为img和video的文件名
        now = time.strftime("%Y-%m-%d-%H-%M-%S", time.localtime(time.time()))
        # if vid_cap:  # video
        fps = self.cap.get(cv2.CAP_PROP_FPS)
        w = int(self.cap.get(cv2.CAP_PROP_FRAME_WIDTH))
        h = int(self.cap.get(cv2.CAP_PROP_FRAME_HEIGHT))
        # 视频检测结果存储位置
        save_path = self.output_folder + 'video_output/' + now + '.mp4'
        return fps, w, h, save_path

    # 定义视频帧显示操作
    def show_video_frame(self):
        name_list = []
        flag, img = self.cap.read()

        # 显示视频数据的帧数
        self.count += 1
        if self.count % 10 == 0:
            self.count = 0
            fps = int(30 / (time.time() - self.start_time))
            self.ui.fps_label.setText('fps:' + str(fps))
            self.start_time = time.time()

        if img is not None:
            # 原始数据的显示
            self.origin = cv2.cvtColor(img, cv2.COLOR_BGR2BGRA)
            self.origin = cv2.resize(self.origin, (640, 480), interpolation=cv2.INTER_AREA)
            self.QtImg_origin = QtGui.QImage(self.origin.data, self.origin.shape[1], self.origin.shape[0],
                                             QtGui.QImage.Format_RGB32)
            self.ui.label_origin.setPixmap(QtGui.QPixmap.fromImage(self.QtImg_origin))
            self.ui.label_origin.setScaledContents(True)  # 设置图像自适应界面大小

            # 检测数据的显示
            info_show = self.detect(name_list, img)  # 检测结果写入到原始img上
            self.vid_writer.write(img)  # 检测结果写入视频
            print(info_show)
            # 检测信息显示在界面
            self.ui.textBrowser_detect.append(info_show)
            show = cv2.resize(img, (640, 480))  # 直接将原始img上的检测结果进行显示
            self.result = cv2.cvtColor(show, cv2.COLOR_BGR2RGB)
            showImage = QtGui.QImage(self.result.data, self.result.shape[1], self.result.shape[0],
                                     QtGui.QImage.Format_RGB888)
            self.ui.label_detect.setPixmap(QtGui.QPixmap.fromImage(showImage))
            self.ui.label_detect.setScaledContents(True)  # 设置图像自适应界面大小
        else:
            self.timer_video.stop()
            # 读写结束,释放资源
            self.cap.release() # 释放video_capture资源
            self.vid_writer.release() # 释放video_writer资源
            self.ui.label.clear()
            # 视频帧显示期间,禁用其他检测按键功能
            self.ui.btn_loadvideo.setDisabled(True)
            self.ui.btn_loadimg.setDisabled(True)
            self.ui.btn_opencamera.setDisabled(True)

    '''显示图片'''
    def show_image(self):
        flag, self.image = self.cap.read()  # 从视频流中读取图片
        image_show = cv2.resize(self.image, (620, 420))  # 把读到的帧的大小重新设置为显示的窗口大小
        width, height = image_show.shape[:2]  # 行:宽,列:高
        image_show = cv2.cvtColor(image_show, cv2.COLOR_BGR2RGB)  # opencv读的通道是BGR,要转成RGB
        image_show = cv2.flip(image_show, 1)  # 水平翻转,因为摄像头拍的是镜像的。
        # 把读取到的视频数据变成QImage形式(图片数据、高、宽、RGB颜色空间,三个通道各有2**8=256种颜色)
        self.photo= QtGui.QImage(image_show.data, height, width, QImage.Format_RGB888)
        self.ui.label_origin.setPixmap(QPixmap.fromImage(self.photo))  # 往显示视频的Label里显示QImage
        self.ui.label_origin.setScaledContents(True)  # 图片自适应

    # 使用摄像头检测
    def button_camera_open(self):
        self.camera_detect = True
        self.ui.textBrowser_print.append("打开摄像头")
        # 设置使用的摄像头序号,系统自带为0
        camera_num = 0
        # 打开摄像头
        self.cap = cv2.VideoCapture(camera_num)
        # 判断摄像头是否处于打开状态
        bool_open = self.cap.isOpened()
        if not bool_open:
            QtWidgets.QMessageBox.warning(self, u"Warning", u"打开摄像头失败", buttons=QtWidgets.QMessageBox.Ok,
                                          defaultButton=QtWidgets.QMessageBox.Ok)
        else:
            QtWidgets.QMessageBox.information(self, u"Warning", u"打开摄像头成功", buttons=QtWidgets.QMessageBox.Ok,
                                          defaultButton=QtWidgets.QMessageBox.Ok)
            self.ui.btn_loadvideo.setDisabled(True)
            self.ui.btn_loadimg.setDisabled(True)

    # 启动摄像头检测
    def button_camera_detect(self):
        self.ui.textBrowser_print.append("启动摄像头检测")
        fps, w, h, save_path = self.set_video_name_and_path()
        fps = 5  # 控制摄像头检测下的fps,Note:保存的视频,播放速度有点快,我只是粗暴的调整了FPS
        self.vid_writer = cv2.VideoWriter(save_path, cv2.VideoWriter_fourcc(*'mp4v'), fps, (w, h))
        self.timer_video.start(30)
        self.ui.btn_loadvideo.setDisabled(True)
        self.ui.btn_loadimg.setDisabled(True)
        self.ui.btn_opencamera.setDisabled(True)


    # 视频暂停按钮
    def button_stop(self):

        self.timer_video.blockSignals(False)
        # 暂停检测
        # 若QTimer已经触发,且激活
        if self.timer_video.isActive() == True and self.num_stop % 2 == 1:
            self.ui.btn_stop.setText('继续')
            self.ui.textBrowser_print.append("视频暂停播放")
            self.num_stop = self.num_stop + 1  # 调整标记信号为偶数
            self.timer_video.blockSignals(True)
            # 继续检测
        else:
            self.num_stop = self.num_stop + 1
            self.ui.btn_stop.setText('暂停')
            self.ui.textBrowser_print.append("视频继续播放")

    # 停止视频播放
    def button_over(self):
        self.ui.textBrowser_print.append("视频结束播放")
        self.cap.release()  # 释放video_capture资源
        self.timer_video.stop()  # 停止读取
        self.timer_photo.stop()  # 停止读取
        if self.vid_writer != None:
            self.vid_writer.release()  # 释放video_writer资源

        self.ui.label_origin.clear()  # 清空label画布
        self.ui.label_detect.clear()  # 清空label画布
        # 启动其他检测按键功能
        self.ui.btn_loadvideo.setDisabled(False)
        self.ui.btn_loadimg.setDisabled(False)
        self.ui.btn_opencamera.setDisabled(False)

        # 结束检测时,查看暂停功能是否复位,将暂停功能恢复至初始状态
        # Note:点击暂停之后,num_stop为偶数状态
        if self.num_stop % 2 == 0:
            print("Reset stop/begin!")
            self.ui.btn_stop.setText(u'暂停')
            self.num_stop = self.num_stop + 1
            self.timer_video.blockSignals(False)

    # 关闭摄像头
    def button_closecamera(self):
        self.ui.textBrowser_print.append("关闭摄像头")
        self.ui.fps_label.setText("帧率")
        self.timer_video.stop()  # 停止读取
        self.timer_photo.stop()  # 停止读取
        self.cap.release()  # 释放摄像头
        self.ui.label_origin.clear()  # 清空label画布
        self.ui.label_detect.clear()  # 清空label画布
        self.cap = cv2.VideoCapture(0, cv2.CAP_DSHOW)  # 摄像头

        self.ui.btn_loadvideo.setDisabled(False)
        self.ui.btn_loadimg.setDisabled(False)
        self.ui.btn_opencamera.setDisabled(False)

    # 拍照
    def button_takephoto(self):
        self.ui.textBrowser_print.append("启动拍照")
        self.timer_photo.start(30)
        self.show_image()
        if self.cap.isOpened():
            FName = "data/images" + fr"/img{time.strftime('%Y%m%d%H%M%S', time.localtime())}"
            print(FName)
            # 原始数据的显示
            flag, self.image = self.cap.read()  # 从视频流中读取图片
            image_show = cv2.resize(self.image, (640, 480))  # 把读到的帧的大小重新设置为显示的窗口大小
            image_show = cv2.cvtColor(image_show, cv2.COLOR_BGR2RGB)  # opencv读的通道是BGR,要转成RGB
            image_show = cv2.flip(image_show, 1)  # 水平翻转,因为摄像头拍的是镜像的。
            # 把读取到的视频数据变成QImage形式(图片数据、高、宽、RGB颜色空间,三个通道各有2**8=256种颜色)
            self.showImage = QtGui.QImage(image_show.data, image_show.shape[1], image_show.shape[0], QImage.Format_RGB888)
            self.ui.label_detect.setPixmap(QtGui.QPixmap.fromImage(self.photo))
            self.ui.label_detect.setScaledContents(True)  # 设置图像自适应界面大小
            self.showImage.save(FName + ".jpg", "JPG", 300)
        else:
            QMessageBox.critical(self, '错误', '摄像头未打开!')
            return None

    # 调用lablelimg批注工具
    def button_labelimg(self):
        self.ui.textBrowser_print.append("启动标注工具")
        os.system("labelimg")

    # 清除显示区域
    def button_clear(self):
        self.ui.textBrowser_print.append("清除显示区域")
        self.ui.textBrowser_print.clear()
        self.ui.textBrowser_detect.clear()


    # 窗口居中
    def center(self):
        qr = self.frameGeometry()
        cp = QDesktopWidget().availableGeometry().center()
        qr.moveCenter(cp)
        # 设置窗口大小
        self.move(qr.topLeft())

    # 关闭事件
    def closeEvent(self, event) -> None:
        reply = QMessageBox.question(self, 'Message',
                                     "Are you sure to quit?",
                                     QMessageBox.Yes | QMessageBox.No, QMessageBox.No)

        if reply == QMessageBox.Yes:
            if self.cap.isOpened():
                self.cap.release()
            if self.timer_video.isActive():
                self.timer_video.stop()
            if self.timer_photo.isActive():
                self.timer_photo.stop()
            event.accept()
        else:
            event.ignore()


from qt_material import apply_stylesheet
if __name__ == '__main__':
    # QtCore.QCoreApplication.setAttribute(QtCore.Qt.AA_EnableHighDpiScaling)  # 自适应分辨率
    app = QtWidgets.QApplication(sys.argv)
    current_ui = UI_Logic_Window()
    current_ui.show()
    extra = {
    # Font
        'font_family': 'Adobe 华文仿宋 Std L',
        'font_size': 14,
    }
    apply_stylesheet(app, theme='light_blue.xml',extra=extra)
    # apply_stylesheet(app, theme='dark_teal.xml')
    sys.exit(app.exec_())

7 使用

7.1. 注意在使用时需要先选择权重

需要先选择权重
烟雾和火灾检测从零开始使用YOLOv5+PyQt5+OpenCV实现(支持图片、视频、摄像头实时检测)

7.2.图片、视频、摄像头实时检测

图片检测效果图

图片检测效果
烟雾和火灾检测从零开始使用YOLOv5+PyQt5+OpenCV实现(支持图片、视频、摄像头实时检测)
视频检测效果
烟雾和火灾检测从零开始使用YOLOv5+PyQt5+OpenCV实现(支持图片、视频、摄像头实时检测)

到了这里,关于烟雾和火灾检测从零开始使用YOLOv5+PyQt5+OpenCV实现(支持图片、视频、摄像头实时检测)的文章就介绍完了。如果您还想了解更多内容,请在右上角搜索TOY模板网以前的文章或继续浏览下面的相关文章,希望大家以后多多支持TOY模板网!

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处: 如若内容造成侵权/违法违规/事实不符,请点击违法举报进行投诉反馈,一经查实,立即删除!

领支付宝红包 赞助服务器费用

相关文章

  • YOLOv8/YOLOv7/YOLOv5-火灾检测、烟雾检测系统-界面+视频实时检测+数据集(算法-毕业设计)

    本项目通过yolov8/yolov7/yolov5训练自己的数据集,并开发可视化界面,实现了一个火灾烟雾实时检测系统,操作视频和效果展示如下: 【yolov8/yolov7/yolov5火灾烟雾检测系统-界面+视频实时检测+数据集(原创算法-毕业设计)】 https://www.bilibili.com/video/BV1FG41127H3/?share_source=copy_webvd_sou

    2024年02月04日
    浏览(40)
  • YOLOv5入门实践(5)——从零开始,手把手教你训练自己的目标检测模型(包含pyqt5界面)

      通过前几篇文章,相信大家已经学会训练自己的数据集了。本篇是YOLOv5入门实践系列的最后一篇,也是一篇总结,我们再来一起按着 配置环境--标注数据集--划分数据集--训练模型--测试模型--推理模型 的步骤,从零开始,一起实现自己的目标检测模型吧! 前期回顾: YOLO

    2023年04月26日
    浏览(43)
  • 是否佩戴安全帽识别从零开始使用YOLOv5+PyQt5+OpenCV实现

    全流程 教程,从数据采集到模型使用到最终展示。若有任何疑问和建议欢迎评论区讨论。 先放上最终实现效果 检测效果 在施工现场,对于来往人员,以及工作人员而言,安全问题至关重要。而安全帽更是保障施工现场在场人员安全的第一防线,因此需要对场地中的人员进行

    2024年02月03日
    浏览(31)
  • YOLOv5目标检测:ubuntu1804从零开始使用YOLOv5训练自己的数据集(亲测有效,一步一步来一定行)

    (1)首先需要安装Anaconda,这个网上教程太多了,下载最新版本就行,在这里就不在赘述了。 (2)安装Pytorch 1. 首先创建python3.6以上版本的conda环境,在这里我用的是python3.8,环境名称为mypytorch 2. 激活创建好的conda环境 3.在PyTorch官网上选择指定版本安装Pytorch Install PyTorch: h

    2024年02月19日
    浏览(46)
  • 【深度学习目标检测】七、基于深度学习的火灾烟雾识别(python,目标检测,yolov8)

    YOLOv8是一种物体检测算法,是YOLO系列算法的最新版本。 YOLO(You Only Look Once)是一种实时物体检测算法,其优势在于快速且准确的检测结果。YOLOv8在之前的版本基础上进行了一系列改进和优化,提高了检测速度和准确性。 YOLOv8采用了Darknet-53作为其基础网络架构。Darknet-53是一

    2024年04月13日
    浏览(40)
  • 基于yolov5-master和pyqt5的森林火灾监测软件

    火灾作为威胁人类生命生产安全的隐患之一,一直是人们关注的重点。传统的火灾监测装置根据温度来检测火灾,不仅灵敏度差,而且反馈时间长,常常会出现消防员收到警报消息时,火室已经无法控制。 森林火灾监测系统的设计与实现是一项基于深度学习技术的创新性研究

    2024年01月22日
    浏览(28)
  • 一文详解Yolov5——基于Yolov5的火灾检测系统

    ✨ 原创不易,还希望各位大佬支持一下 textcolor{blue}{原创不易,还希望各位大佬支持一下} 原创不易,还希望各位大佬支持一下 👍 点赞,你的认可是我创作的动力! textcolor{green}{点赞,你的认可是我创作的动力!} 点赞,你的认可是我创作的动力! ⭐️ 收藏,你的青睐是

    2024年02月03日
    浏览(23)
  • 基于YOLOv5的火焰烟雾检测算法实战

    一个不知名大学生,江湖人称菜狗 original author: Jacky Li Email : 3435673055@qq.com Time of completion:2023.6.11 Last edited: 2023.6.11 导读: 火焰烟雾检测是智慧安防业务场景中重要的功能之一,本文提出了一种基于YOLOv5的火焰烟雾检测算法,具有更强的泛化性,对火焰烟雾的检测效果更加准

    2024年02月09日
    浏览(28)
  • 【yolov5&yolov7&yolov8火焰和烟雾检测】

    YOLOv5训练好的火焰检测模型,并包含2000张标注好的火焰和烟雾数据集,标签格式为xml和txt两种,类别名为fire, 有QT界面 采用pytrch框架,代码是python的 火灾检测数据集-1 YOLOv3火焰识别训练模型: https://download.csdn.net/download/zhiqingAI/85438269 yolov5火焰识别训练模型+数据集: https

    2024年04月28日
    浏览(29)
  • 智能火焰与烟雾检测系统(Python+YOLOv5深度学习模型+清新界面)

    摘要:智能火焰与烟雾检测系统用于智能日常火灾检测报警,利用摄像头画面实时识别火焰与烟雾,另外支持图片、视频火焰检测并进行结果可视化。本文详细介绍基于智能火焰与烟雾检测系统,在介绍算法原理的同时,给出 P y t h o n 的实现代码以及 P y Q t 的UI界面。在界面

    2023年04月17日
    浏览(32)

觉得文章有用就打赏一下文章作者

支付宝扫一扫打赏

博客赞助

微信扫一扫打赏

请作者喝杯咖啡吧~博客赞助

支付宝扫一扫领取红包,优惠每天领

二维码1

领取红包

二维码2

领红包