从零开始完成YOLOv5目标识别(三)用PyQt5展示YOLOv5的识别结果

2023-11-13

往期内容

从零开始完成Yolov5目标识别(二)制作并训练自己的训练集

从零开始完成Yolov5目标识别(一)准备工作

目录

往期内容

一、项目框架:

二、核心内容:

1. QtDesign设计:

2. 检测部分

2.1 导包

2.2 main.py要实现的主要功能

三、效果


一、项目框架:

其中main.py和MainWindow.py是pyqt5的功能文件。

二、核心内容:

pyqt5的安装过程略过;

1. QtDesign设计:

用来显示视频、图像和摄像头内容的label、textBrowser和按钮控件采用水平布局;

窗口空白处单击右击-》布局-》水平布局,可以使控件自适应页面大小。

用转换工具使.ui文件转化成python代码

pyuic5.bat -o MainWindow.py MainWindow.ui

2. 检测部分

2.1 导包

import sys
import cv2
import argparse
import random
import torch
import numpy as np
import torch.backends.cudnn as cudnn

from PyQt5 import QtCore, QtGui, QtWidgets
from PyQt5.QtCore import *
from PyQt5.QtGui import *
from PyQt5.QtWidgets import *

from utils.torch_utils import select_device
from models.experimental import attempt_load
from utils.general import check_img_size, non_max_suppression, scale_coords
from utils.datasets import letterbox
from utils.plots import *

util文件夹下plots.py随着YOLOv5版本更新发生了比较大的变化,这个文件主要负责各种图像的绘制。

plots中一些常用的方法:(不完整)

butter_lowpass_filtfilt:做平滑曲线

plot_images:在test.py中绘制预测框‘’

plot_lr_scheduler:train.py中学习率可视化

plot_labels:train.py中对label进行可视化

plot_results:训练结果可视化

为了方便PyQt5调用plots对图像锚框,在plots.py中加入上几个版本的YOLOv5的plot_one_box方法:

def plot_one_box(x, im, color=(128, 128, 128), label=None, line_thickness=3):
    """一般会用在detect.py中在nms之后变量每一个预测框,再将每个预测框画在原图上
    使用opencv在原图im上画一个bounding box
    :params x: 预测得到的bounding box  [x1 y1 x2 y2]
    :params im: 原图 要将bounding box画在这个图上  array
    :params color: bounding box线的颜色
    :params labels: 标签上的框框信息  类别 + score
    :params line_thickness: bounding box的线宽
    """
    # check im内存是否连续
    assert im.data.contiguous, 'Image not contiguous. Apply np.ascontiguousarray(im) to plot_on_box() input image.'
    # tl = 框框的线宽  要么等于line_thickness要么根据原图im长宽信息自适应生成一个
    tl = line_thickness or round(0.002 * (im.shape[0] + im.shape[1]) / 2) + 1  # line/font thickness
    # c1 = (x1, y1) = 矩形框的左上角   c2 = (x2, y2) = 矩形框的右下角
    c1, c2 = (int(x[0]), int(x[1])), (int(x[2]), int(x[3]))
    # cv2.rectangle: 在im上画出框框   c1: start_point(x1, y1)  c2: end_point(x2, y2)
    # 注意: 这里的c1+c2可以是左上角+右下角  也可以是左下角+右上角都可以
    cv2.rectangle(im, c1, c2, color, thickness=tl, lineType=cv2.LINE_AA)
    # 如果label不为空还要在框框上面显示标签label + score
    if label:
        tf = max(tl - 1, 1)  # label字体的线宽 font thickness
        # cv2.getTextSize: 根据输入的label信息计算文本字符串的宽度和高度
        # 0: 文字字体类型  fontScale: 字体缩放系数  thickness: 字体笔画线宽
        # 返回retval 字体的宽高 (width, height), baseLine 相对于最底端文本的 y 坐标
        t_size = cv2.getTextSize(label, 0, fontScale=tl / 3, thickness=tf)[0]
        c2 = c1[0] + t_size[0], c1[1] - t_size[1] - 3
        # 同上面一样是个画框的步骤  但是线宽thickness=-1表示整个矩形都填充color颜色
        cv2.rectangle(im, c1, c2, color, -1, cv2.LINE_AA)  # filled
        # cv2.putText: 在图片上写文本 这里是在上面这个矩形框里写label + score文本
        # (c1[0], c1[1] - 2)文本左下角坐标  0: 文字样式  fontScale: 字体缩放系数
        # [225, 255, 255]: 文字颜色  thickness: tf字体笔画线宽     lineType: 线样式
        cv2.putText(im, label, (c1[0], c1[1] - 2), 0, tl / 3, [225, 255, 255], thickness=tf, lineType=cv2.LINE_AA)

2.2 main.py要实现的主要功能

  • 调用yolov5中的方法,设置权重
def model_init(self):
        # 模型相关参数配置
        parser = argparse.ArgumentParser()
        parser.add_argument('--weights', nargs='+', type=str, default='weights/yolov5s.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='cpu', 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

        # 若openfile_name_model不为空,则使用此权重进行初始化
        if self.openfile_name_model:
            weights = self.openfile_name_model
            print("Using button choose model")

        self.device = select_device(self.opt.device)
        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

        # 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]
        print("model initial done")
        # 设置提示框
        QtWidgets.QMessageBox.information(self, u"Notice", u"模型加载完成", buttons=QtWidgets.QMessageBox.Ok,
                                      defaultButton=QtWidgets.QMessageBox.Ok)
  • detect方法,输入原始图像返回监测信息
def detect(self, name_list, img):
        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 = Annotator.box_label(xyxy, showimg, label=label, color=self.colors[int(cls)], line_thickness=2)
                        #single_info = Annotator.box_label(xyxy, showimg, label=label, color=self.colors[int(cls)])
                        
                        single_info = plot_one_box(xyxy, showimg, label=label, color=self.colors[int(cls)], line_thickness=2)

                        # print(single_info)
                        # info_show = info_show + single_info + "\n"
        return  info_show
  • 检测和显示各帧图像
def show_video_frame(self):
        name_list = []
        flag, img = self.cap.read()
        if img is not None:
            info_show = self.detect(name_list, img) # 检测结果写入到原始img上
            print(info_show)
            # 检测信息显示在界面
            self.ui.textBrowser.setText(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.setPixmap(QtGui.QPixmap.fromImage(showImage))
            self.ui.label.setScaledContents(True)  # 设置图像自适应界面大小

        else:
            self.timer_video.stop()
            self.cap.release()
            self.ui.label.clear()
            # 视频帧显示期间,禁用其他检测按键功能
            self.ui.pushButton_video.setDisabled(False)
            self.ui.pushButton_img.setDisabled(False)
            self.ui.pushButton_camer1.setDisabled(False)
            self.ui.pushButton_camer0.setDisabled(False)
  • 各种控件功能
# 暂停与继续检测
    def button_video_stop(self):
        self.timer_video.blockSignals(False)
        # 暂停检测
        # 若QTimer已经触发,且激活
        if self.timer_video.isActive() == True and self.num_stop%2 == 1:
            self.ui.pushButton_stop.setText(u'暂停检测') # 当前状态为暂停状态
            self.num_stop = self.num_stop + 1 # 调整标记信号为偶数
            self.timer_video.blockSignals(True)
        # 继续检测
        else:
            self.num_stop = self.num_stop + 1
            self.ui.pushButton_stop.setText(u'继续检测')

    # 结束视频检测
    def finish_detect(self):
        # self.timer_video.stop()
        self.cap.release() # 释放cap
        self.ui.label.clear() # 清空label画布
        # 启动其他检测按键功能
        self.ui.pushButton_video.setDisabled(False)
        self.ui.pushButton_img.setDisabled(False)
        self.ui.pushButton_camer1.setDisabled(False)
        self.ui.pushButton_camer0.setDisabled(False)

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

信号与槽不在展示了。

三、效果

特别感谢:使用PyQt5为YoloV5添加界面(一)_叼着狗骨头的猫的博客-CSDN博客_qt yolov5带来的帮助

本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

从零开始完成YOLOv5目标识别(三)用PyQt5展示YOLOv5的识别结果 的相关文章

随机推荐

  • 逗号运算符

    逗号运算符是指在C语言中 多个表达式可以用逗号分开 其中用逗号分开的表达式的值分别结算 但整个表达式的值是最后一个表达式的值 在前端的一些笔试中也可以看到逗号运算符的存在 作为C语言中的运算级别最低的一员 逗号运算符 结合的方向是 从左往右
  • dat文件

    DAT 数字录音带 是一种用于磁带数字录音的专业品质级别的标准媒体和技术 DAT设备就是一个数字磁带录音器 具有与录像机相似的旋转型磁头 大多数的DAT设备都能以44 1千赫 CD音频标准 以及48千赫的采样率来录音 DAT已经成为掌握录音
  • 在Java中如何判断字符串的编码格式

    最近 我一直试图寻找一种判断Java程序中字符串编码格式的方法 同时 也查找了很多资料 设计了一个的程序 美中不足的是该方法对仅含有数字和英文字母的字符串无效 原理 ASCII GBK UTF 8对数字和英文字母的编码相同 对其它字符编码不
  • GD32F105的CAN通讯,可以发送数据,但接收不到数据

    项目简介 使用的芯片型号GD32F105VC 芯片资源CAN1 波特率500k 调试过程中发现发送数据正常 但是接收不到数据 总结几点注意事项如下 1 需要设置滤波器 若未设置滤波器 则接收不到数据 傻傻的认为滤波器配置问题 以为注释掉滤波
  • vue-vuetify-admin案例讲解

    vue vuetify admin案例讲解 1 Introduction 1 1 directory structure 1 2 vue cli 1 3 vuex 1 3 1 在store目录创建index js 1 3 2 在main j
  • 队列(一种遵循先进先出原则的数据结构)

    目录 1 队列 Queue 2 队列的抽象数据类型 队列ADT 3 队列接口 4 利用数组实现队列 4 1 队列的实现 4 2 利用数组实现队列的优势与缺点 5 利用单链表实现队列 5 1 队列的实现 5 2 利用单链表实现队列的优势与缺点
  • js对象的继承

    学无止境 望君把握时间 首先我们需要定义一个类 定义一个动物类 function Animal name 属性 this name name Animal 实例方法 this sleep function console log this
  • js增加class或者删除class

    1 比较传统的方法 var classVal document getElementById id getAttribute class 删除的话 classVal classVal replace someClassName docume
  • GAMES101: 现代计算机图形学入门(2)几何、光线追踪

    GAMES101 现代计算机图形学入门 链接 GAMES101 1 几何 1 1 几何的表示 隐式几何 通过一个函数表达式来表示的几何体 即 f x y z 0 优点 很容易判断一个点在不在几何体上 缺点 很难通过表达式看出几何体的形状 显
  • 菜鸟求职记6

    来到古城已经整整38天了 本想快快的找到工作然后做自己这三年来都没有做的事情 旅游 看电视 打篮球 打乒乓球 可是 事实却并非如此 这一个多月的苦衷可以说是一言难尽呀 到了此时此刻 恐怕每一个人都已经累得奄奄一息了 每个人曾经的自信都被现实
  • StrongSORT:Make DeepSORT Great Again

    1北京邮电大学2中国网络系统与网络文化北京市重点实验室 摘要 现有的多目标跟踪 Multi Object Tracking MOT 方法大致可以分为基于检测的跟踪和联合检测关联两种范式 虽然后者引起了更多的关注 并显示出与前者相当的性能 但
  • 在Android studio中Intent的几种基本使用方法

    在Android开发中 Intent是最基本也是最常用的操作 在Activity Service BroadcastReceiver这些核心组件中也需要Intent进行操作 下面我们具体介绍Intent在开发中的一些基本用法 假定目前有Fi
  • 第三方支付 -----支付宝支付流程

    大家都知道 第三方支付 已经普遍都在使用 所以我今天就说一下支付宝的支付流程 首先进入支付宝平台 点击开发中心 研发服务 获得沙盒的appid以及商户公钥和支付宝公钥 然后利用秘钥生成软件生成私钥和公钥 建立keys文件夹 将私钥和公钥文件
  • 将日期字符串转成LocalDateTime

    如果直接用LocalDateTime parse将日期字符串 yyyy MM dd 转成LocalDateTime会导致报错 所以我这里提供了将日期字符串转成LocalDateTime的方法 仅供参考 如有更好方式 欢迎大家分享 impor
  • WSL2报错:nvidia-smi Command ‘nvidia-smi‘ not found, but can be installed with:

    这里写自定义目录标题 找了很多方法 解决 分割线 WSL2部署 找了很多方法 在社区找了很多方法 结果在b站评论区找到了一个方法给解决了 原本一开始有人说是驱动版本问题 我nvcc V是ok的 但是nvidia smi一直报错 Comman
  • LaTeX排版(一):字体、页眉页脚、页边距、行距的设置

    目录 字体设置 布局设置 页眉页脚设置 行距的设置 其他 字体设置 字体设置需要用到宏包fontspec 需要在导言区添加如下指令 usepackage fontspec 中英文字体都可以分为如下3种 正文字体族 无衬线字体族 打字机字体族
  • 十进制转十六进制 C++

    目录 题目描述 思路分析 AC代码 题目描述 编写一个函数 传入一个十进制的正整数 将十进制整数转换为十六进制的字符串并返回 十六进制字符串中的字母全部大写 输入描述 键盘输入一个十进制的正整数 输出描述 输出该十进制整数转换后的十六进制字
  • 硬盘柱面损坏怎么办_最靠谱的机械硬盘坏道修复工具一:DiskGenius

    DiskGenius是一款硬盘分区 数据修复软件 DiskGenius的功能非常丰富 然而很多时候 我们都只是用DiskGenius来分区硬盘 对硬盘进行一些常规性能的操作 常常忽略了DiskGenius最重要的一个功能 那就是机械硬盘的坏
  • vue 按钮权限

    项目中按钮的操作权限我们可以直接使用 v if 判断就行 但是每个页面都要写一堆判断不太雅观 所以 可以写一个全局函数或者自定义指令 两种方式优雅的实现 一 全局函数 一般在登陆接口中后台就把权限列表信息提供了 可以把他存到缓存或者vuex
  • 从零开始完成YOLOv5目标识别(三)用PyQt5展示YOLOv5的识别结果

    往期内容 从零开始完成Yolov5目标识别 二 制作并训练自己的训练集 从零开始完成Yolov5目标识别 一 准备工作 目录 往期内容 一 项目框架 二 核心内容 1 QtDesign设计 2 检测部分 2 1 导包 2 2 main py