linux桌面小程序开发日记4(pyqt5+yolov5)

2023-11-02

linux桌面小程序开发日记4

修改detect.py文件,让yolov5连接摄像头,同时输出识别出来的内容

最后一篇博客地址:https://blog.csdn.net/Liuchengzhizhi/article/details/123692365

B站视频:https://www.bilibili.com/video/BV1rZ4y1B7t8?share_source=copy_web

源码:https://gitee.com/wx_b915676bb6/yolo-pyqt.git

前言

承接上回,我们把跑yolo的环境安装好啦,接下来就是要对配置文件动刀,让他嵌入在我们的程序里啦

任务要求——(将模型嵌入在这个小程序里)

  • 配置摄像头,让他使用摄像头中的内容(预计会卡,因为用的是CPU而且还是只分配了3个核心的CPU)
  • 输出识别的内容(看情况,如果这个日记内容多的话,就放在下一个日记里)

我们开始我们这一章的任务吧

第一步 尝试yolov5连接摄像头

判断摄像头能不能用

这个没想到,很简单很简单,只需要找到detect.py 文件,找到source这个地方,然后将default修改为0就可以了。然后就能够使用摄像头了。(编号0代表的就是本地摄像头啦)(处理的速度没想到也不是太慢!)
在这里插入图片描述

第二步 解读一下detect.py文件

个人觉得,不管怎么样,总得过一遍detect.py的代码。不管读不读的懂。

参考博客如下

(YOLOV5检测代码detect.py注释与解析_Q1u1NG的博客-CSDN博客_detect

【YOLOV5-5.x 源码解读】detect.py_满船清梦压星河HK的博客-CSDN博客_yolov5源码

yolov5检测视频流的原理、detect.py解读_RocZhang的博客-CSDN博客_yolov5视频检测

读下来后,我的样子就像下图那样!(所以我也不知道看了这三篇博客后用处大不大,嘿嘿)

然后我们继续第三步吧!!

在这里插入图片描述

第三步 pyqt5和yolov5结合

这里真的是让我崩溃的地方,尝试了好多好多办法,但是都没用!!!

最后,我找到了我的救命稻草!!!

作者:叼着狗骨头的猫 您就是我的偶像,yyds!!!!

使用PyQt5为YoloV5添加界面(一)_wrh975373911的博客-CSDN博客_qt yolov5

通过这篇博客,我成功的将yolov5放在了我的界面程序里!!!!

这里主要的文件detect_logical.py文件 (基于上面叼着骨头的猫改动的)(会有些混乱)

期间遇到的主要问题:

  • 如何将上面的界面,换成我的界面
  • 没有总是提示没有找到opt(加载的智能模型)
    • 因为自己在代码中,没有选择模型,也没有进行模型初始化(原程序是手动选择模型,加载模型的)
  • 按钮绑定的问题
  • 列表数据加载的问题

代码如下:

下面的代码是可以放在“叼着狗骨头的猫”博客中提供的项目里运行的(只需要把模型文件路径修改一下)

就是这边的一句话

 # 权重初始文件名
        #模型路径
        self.openfile_name_model = "yolov5-ma/weights/yolov5s.pt"
        self.model_init()
# -*- coding: utf-8 -*-
# @Modified by: Ruihao
# @ProjectName:yolov5-pyqt5

from cProfile import label
import sys
import cv2
import time
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 plot_one_box2
from utils.torch_utils import select_device, load_classifier, time_synchronized


class Ui_MainWindow(QtWidgets.QWidget):
    def __init__(self,parent=None):
        super().__init__(parent) #父类的构造函数
 
        self.timer_video = QtCore.QTimer() #定义定时器,用于控制显示视频的帧率
        self.cap = cv2.VideoCapture()       #视频流
 
        self.set_ui()                       #初始化程序界面
        self.slot_init()                    #初始化槽函数

        self.num_stop = 1 # 暂停与播放辅助信号,note:通过奇偶来控制暂停与播放
        self.output_folder = 'output/'
        self.vid_writer = None

        # 权重初始文件名
        #模型路径
        self.openfile_name_model = "yolov5-ma/weights/yolov5s.pt"
        self.model_init()
        # 打开摄像头
        self.button_camera_open()


    '''程序界面布局'''
    def set_ui(self):
        self.__layout_main = QtWidgets.QGridLayout()           #总布局
        self.__layout_fun_button1 = QtWidgets.QHBoxLayout()      #按键布局1
        self.__layout_fun_button2 = QtWidgets.QHBoxLayout()      #按键布局2
        self.__layout_data_show = QtWidgets.QVBoxLayout()       #数据(视频)显示布局
        self.__layout_list_show  = QtWidgets.QVBoxLayout()          #表格布局

        self.button_confirm = QtWidgets.QPushButton('确认') #建立用于打开摄像头的按键
        self.button_settle_accounts = QtWidgets.QPushButton('结账') #建立结账的按钮
        self.list_show =  QtWidgets.QTableWidget(6,3)                          #建立表格
        self.label_account = QtWidgets.QLabel("总价")                          #建立label

        '''set butten size '''
        self.button_confirm.setMinimumHeight(50)                #设置按键大小
        self.button_settle_accounts.setMinimumHeight(50)
        self.label_account.setMinimumHeight(50)
        

        # self.button_close.move(10,100)                      #移动按键  这句话去掉好像也没关系
        '''设置标签的格式'''
        font = QtGui.QFont()
        font.setPixelSize(18)
        self.label_account.setFont(font)

        '''设置表格'''
        self.list_show.setHorizontalHeaderLabels(["名称","数量","单价"])
        self.list_show.horizontalHeader().setSectionResizeMode(QHeaderView.Stretch)# adaptive size
        self.list_show.setEditTriggers(QAbstractItemView.EditTrigger(False))    #将表格的内容设为不可编辑
        '''信息显示'''
        self.label_show_camera = QtWidgets.QLabel()   #定义显示视频的Label
        self.label_show_camera.setFixedSize(641,481)    #给显示视频的Label设置大小为641x481
        '''把按键加入到按键布局中'''
        self.__layout_fun_button1.addWidget(self.button_confirm) #把重新确认的按键放到按键布局中
        # self.__layout_fun_button1.addWidget(self.button_close)       #把退出程序的按键放到按键布局中
        self.__layout_fun_button2.addWidget(self.button_settle_accounts) #把结账的按键放到按键布局中
        '''把表格加入到表格布局中'''
        self.__layout_list_show.addWidget(self.list_show)           #将表格添加到表格布局中
        self.__layout_list_show.addWidget(self.label_account) #将总价label添加到表格布局中


        '''把某些控件加入到总布局中'''
        self.__layout_main.addLayout(self.__layout_list_show,0,0)            #将表格布局添加到总布局中
        self.__layout_main.addLayout(self.__layout_fun_button1,1,1)      #把按键布局加入到总布局中
        self.__layout_main.addLayout(self.__layout_fun_button2,1,0)      #把按键布局加入到总布局中
        self.__layout_main.addWidget(self.label_show_camera,0,1)        #把用于显示视频的Label加入到总布局中
        
        '''总布局布置好后就可以把总布局作为参数传入下面函数'''
        self.setLayout(self.__layout_main) #到这步才会显示所有控件
 
    
 
    '''初始化所有槽函数'''
    def slot_init(self):
        
        self.button_confirm.clicked.connect(self.button_video_stop)    #若该按键被点击,则调用button_confirm()
        self.timer_video.timeout.connect(self.show_video_frame) # 定时器超时,将槽绑定至show_video_frame
    
    # ========================================================


    # 加载相关参数,并初始化模型
    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='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
        #  Second-stage classifier
        classify = False
        if classify:
            modelc = load_classifier(name='resnet101', n=2)  # initialize
            modelc.load_state_dict(torch.load('weights/resnet101.pt', map_location=self.device)['model']).to(self.device).eval()

        # 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)




    # 目标检测
    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 = ""

            # 定义标签变量名
            label_names = ""

            # 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)
                        # print(single_info)
                        info_show = info_show + single_info + "\n"

                        # 新添加的标签信息,提交给pyqt5 处理
                        label_names = label_names+","+self.names[int(cls)]
            # 转化为数组    
            label_names = label_names.split(",")
            # 去掉空值
            label_names = [i for i in label_names if(len(str(i))!=0)]

        return  info_show , label_names

    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 button_camera_open(self):
        

        print("Open camera to detect")
        # 设置使用的摄像头序号,系统自带为0
        camera_num = 0
        # 打开摄像头
        self.cap = cv2.VideoCapture(camera_num)
        # 判断摄像头是否处于打开状态
        bool_open =self.cap.open(camera_num)
        if not bool_open:
            QtWidgets.QMessageBox.warning(self, u"Warning", u"打开摄像头失败", buttons=QtWidgets.QMessageBox.Ok,
                                          defaultButton=QtWidgets.QMessageBox.Ok)
        else:
            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)

    # 定义视频帧显示操作
    def show_video_frame(self):
        name_list = []
        flag, img = self.cap.read()
        if img is not None:
            info_show , label_names = self.detect(name_list, img) # 检测结果写入到原始img上
            self.vid_writer.write(img) # 检测结果写入视频
            print(info_show)

            # print(label_names)
            self.set_labels(label_names)
            # 检测信息显示在界面
            # 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.label_show_camera.setPixmap(QtGui.QPixmap.fromImage(showImage))  #往显示视频的Label里 显示QImage
            # self.ui.label.setScaledContents(True)  # 设置图像自适应界面大小

        else:
            self.timer_video.stop()
            # 读写结束,释放资源
            self.cap.release() # 释放video_capture资源
            self.vid_writer.release() # 释放video_writer资源
            self.ui.label.clear()
            # 视频帧显示期间,禁用其他检测按键功能
            self.button_settle_accounts.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.button_confirm.setText(u'重新确认') # 当前状态为暂停状态
            self.num_stop = self.num_stop + 1 # 调整标记信号为偶数
            self.timer_video.blockSignals(True)
            # 启动结账按钮
            self.button_settle_accounts.setDisabled(False)
        # 继续检测
        else:
            self.num_stop = self.num_stop + 1
            self.button_confirm.setText(u'确认')
            self.button_settle_accounts.setDisabled(False)


# 处理label集的函数
    def set_labels(self ,labels ):
        self.list_show.clearContents()
        labels = np.array(labels)
        for  i in range(len(labels)):
            label = labels[i]
            
            newItem  = QTableWidgetItem(labels[i])
            self.list_show.setItem(i,0,newItem)
            self.list_show.setItem(i,1,QTableWidgetItem("1"))
       

if __name__ == '__main__':
    app =  QtWidgets.QApplication(sys.argv)
    ui =Ui_MainWindow()                 #实例化Ui_MainWindow
    ui.show()                               #调用ui的show()以显示。同样show()是源于父类QtWidgets.QWidget的
    # current_ui = UI_Logic_Window()
    # current_ui.show()
    sys.exit(app.exec_())

最后运行程序,显示的结果是这个样子。

在这里插入图片描述

当点击确认,摄像头就会暂停列表上的数据也就会固定。

这里的代码很多很复杂,很多都是我借来用的,所以没有能力进行一个详细的解读。(等到了暑假我把这个项目完整的做完,在上传到云盘分享吧)

总结

这一部分,算是我代码中最核心的部分,真的踩了很多坑。有很多话想要说,但不知道为什么,又不知道该写什么。那就不写了

那么也从这一章,我的日记也终于可以发表出去了。我也可以安心的准备我的专科升本科的考试了。

希望我的这个日记对你们也有帮助。

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

linux桌面小程序开发日记4(pyqt5+yolov5) 的相关文章

随机推荐

  • 阿里云服务器开放宝塔面板8888端口配置安全组教程

    默认情况下 阿里云处于安全考虑 仅开放了80和3389端口 云吞铺子以开放8888端口为例 1 登录到云服务器ECS控制台 2 在实例列表中 点击 更多 网络和安全组 安全组配置 或者在这 3 手动添加安全组规则 云吞铺子以添加宝塔面板管理
  • ubuntu16.04安装opencv3.4.5时出错及解决方法

    ubuntu16 04安装opencv3 4 5时 参考这篇文章 运行 cmake D CMAKE BUILD TYPE Release D CMAKE INSTALL PREFIX usr local 出现了错误 CMake Error
  • Go语言基础(一)

    Go语言具有支持高并发 语法简洁等特点 变量 Go语言中变量的定义可以直接定义 不需要基本数据类型 这些会自动帮你定义 但是你也可以写出变量的类型 放在变量的后面 还有一种是直接使用 来定义变量 var a initial var b c
  • spring cloud 通过Ribbon配置负载均衡 和Hystrix熔断器

    前言 本篇博客是基于上一篇博客的再度扩展springcloud项目配置 Ribbon是用于实现负载均衡的 Hystrix是用于超时的方法返回一个兜底数据的 Ribbon配置 1 在RestTemplate上加注解 LoadBalanced
  • C++类中嵌套enum(枚举)、union(联合)、struct(结构体)类型

    1 枚举 include
  • 一款非常萌的桌面工具---bongo cat mver0.1.6 附使用教程

    bongo cat mver是一款画风非常萌的桌面工具 由B站用户原创制作 具有人性化UI设置界面 可用于装饰视频或直播 能够根据鼠标 键盘操作做出相应的动作 超级可爱 该软件是以小猫的形式展现 共有四种模式供用户选择 包括键鼠 绘画 手柄
  • 5G+AIoT“零碳”解决方案

    碳交易 目前被认为是用市场机制调整碳排放的有效工具 通过有限的碳排放量分配 政府部门可以对碳排放配额进行总量控制 使纳入市场的控排企业受到碳排放限额的限制 排放量少于配额的企业可将多余的配额出来销售 反之超配额的企业 就需要到市场上去购买排
  • debian linux vnc,在Debian 9上,如何安装和配置VNC

    本教程描述了如何在Debian 9上安装和配置VNC服务器 我们还将向您展示如何创建SSH隧道并安全地连接到VNC服务器 先决条件 在继续本教程之前 请确保您以具有sudo特权的用户身份登录 安装桌面环境 你的Debian server可能
  • IOS9中出现的错误

    1 Bitcode 错误提示 ld Applications Cocos frameworks cocos2d x 3 8 1 prebuilt ios libcocos2d iOS a CCEAGLView ios o does not
  • vue3之后台管理系统权限

    权限概括 后台管理系统中权限是不可少的一部分 例如 页面权限 菜单权限 按钮权限 路由权限等 文章目录 权限概括 定义权限 一 用户登录和认证 二 前端路由控制 三 菜单权限 四 数据级别的权限控制 五 前端界面的反馈 六 按钮权限 实现思
  • java+selenium3

    一 环境搭建 1 JDK安装 配置环境变量 2 selenium下载 3 测试Demo public static void main String args throws InterruptedException todo System
  • 华为机试—字符串处理专题

    文章目录 leetbook 字符串 125 验证回文串 5 最长回文子串 131 分割回文串 见回溯 HJ1计算字符串最后一个单词的长度 单词以空格隔开 HJ2输出输入字符串中含有该字符的个数 HJ4字符串分隔 连续输入多行字符串所以用ge
  • 硬件基础元器件【1.电阻篇】

    文章目录 1 电阻 1 1 电阻的作用 1 2 电阻选型要点 1 3 电阻的主要使用场景 1 3 1 上 下拉电阻 上下拉电阻作用 阻值选择原则 1 3 2 MOS管栅极驱动电阻 1 3 3 电源反馈电阻 1 3 4 晶振并联电阻 1 3
  • 算法(C++):加一

    算法 C 加一 题目难度 简单 题目描述 给定一个由 整数 组成的 非空 数组所表示的非负整数 在该数的基础上加一 最高位数字存放在数组的首位 数组中每个元素只存储单个数字 你可以假设除了整数 0 之外 这个整数不会以零开头 示例1 输入
  • centos7下docker中mysql大小写敏感相关问题解决方案

    因为linux下的mysql默认区分大小写 而windows下的mysql默认不区分 所以关联的时候 有时候会出问题 所以我们要取消linux小的区分大小写 目录 一 mysql大小写解决方案 1 启动docker 2 运行mysql容器
  • zabbix监控TCP连接状态

    一 zabbix监控TCP连接状态 1 取到TCP连接状态的值 root web01 netstat antp awk NR gt 2 print 6 grep TIME WAIT wc l 可以取到TIME WAIT的个数 依次类推 可以
  • linux安装mysql-8.0.11出现错误

    linux安装mysql 8 0 11出现 2020 04 16T11 47 06 723455Z 0 Warning MY 011070 Server Disabling symbolic links using skip symboli
  • java基础经典题——猴子吃桃

    作为学java循环的经典问题 猴子第一天摘了若干个桃子 当即吃了一半 还不解馋 又多吃了一个 第二天 吃剩下的桃子的一半 还不过瘾 又多吃了一个 以后每天都吃前一天剩下的一半多一个 到第10天想再吃时 只剩下一个桃子了 问第一天共摘了多少个
  • Ubuntu系统中如何删除一个用户

    1 打开终端命令行 运用userdel命令删除指定的用户 注意要加sudo权限指令 如下图 sudo userdel ascend 2 在删除的时候一定注意是在管理员目录下的 普通的用户是没有这个权限的 3 删除指令执行完了以后怎么判断是否
  • linux桌面小程序开发日记4(pyqt5+yolov5)

    linux桌面小程序开发日记4 修改detect py文件 让yolov5连接摄像头 同时输出识别出来的内容 最后一篇博客地址 https blog csdn net Liuchengzhizhi article details 12369