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

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_())

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

在这里插入图片描述

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

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

总结

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

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

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

  • 3
    点赞
  • 24
    收藏
    觉得还不错? 一键收藏
  • 6
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 6
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值