PySide2开发“安全帽检测”界面遇到的问题及其解决方案

0 说明

系统:Win10

python版本:python 3.7.4

tensorflow-gpu: 14.0

cuda版本:10.0

cuDNN版本:7.4.0

目标检测模型:yolo v3

1 实现的界面

功能说明:在开始检测之前,需要手动选择视频进行检测,当点击“开始检测”的时候,会使用子线程进行yolov3目标检测,然后在主线程中更新界面(注意,一定要在子线程中处理耗时的操作,在主线程中进行界面的更新),主线程和子线程是采用signal和slot的通信方式。

2 主要内容(遇到的问题及其解决方案)

2.1 在子线程中进行检测,在主线程中更新界面

最初的时候,我是重写python中的Thread类来开辟一个子线程进行处理目标检测耗时操作,需要循环读取视频,也就是需要在子线程中循环发送signal。但是在实验的时候,slot无法接收到signal发送到的信息,具体原因不明来。在网上查找相关资料之后,使用PySide2中自带的QThread来开辟一个子线程进行处理。

在使用QThread进行检测和更新的时候,有两种方式可以实现(这边以循环发送signal为例)

第一种方式:将Signal和QThread进行绑定

# 创建Signal的子类
class DemoSignal(QObject):
    msg = Signal(str)

    def __init__(self):
        super().__init__()

    def run(self):
        for i in range(100):
            self.msg.emit('这是信号{}'.format(i))


# 创建QThread的子类
class DemoQThread(QThread):
    def __init__(self):
        super().__init__()


class Status(QWidget):
    def __init__(self):
        super().__init__()
        # 创建Signal和QThread的实例对象
        self.signal = DemoSignal()
        self.qThread = DemoQThread()
        # 设置signal的slot
        self.signal.msg.connect(self.receiveSignal)
        # 将signal放到子线程中
        self.signal.moveToThread(self.qThread)
        # 将signal和qthread进行绑定
        self.qThread.started.connect(self.signal.run)
        self.qThread.start()

第二种方式:将Signal作为QThread的一个属性

 

# 创建QThread的子类
class DemoQThread(QThread):
    msg = Signal(str)

    def __init__(self):
        super().__init__()

    def run(self):
        for i in range(1000):
            self.msg.emit('这是信号{}'.format(i))



# 创建一个显示界面
class Status(QWidget):
    def __init__(self):
        super().__init__()
        # 实例化QThread
        # 注意一定要有self
        self.qthread = DemoQThread()
        self.qthread.msg.connect(self.receiveSignal)
        self.qthread.start()

参考资料地址:https://www.bilibili.com/video/BV154411n79k?p=88

 

2.2 使用pyinstaller进行封装成exe文件的时候,无法正常显示图片资源

在做界面的时候,我用到以一些图片来美化界面的显示,但是运行exe文件之后,无法正常显示,这是因为pyintaller在封装成exe文件的时候,并不会将你的资源文件也进行封装,也就是说你的png,jpg等资源文件并会封装在dist中,exe文件是找不到相关图片或者相关资源进行显示的。所以为了解决这个问题,我们可以将资源文件保存在py文件中,然后导入相关的py文件就可以了。

但是,在下面进行封装的时候pyinstaller无法识别自定义的模块,需要进行额外的处理,就把这个步骤通过另外一种方式给覆盖覆盖掉了,也就是说,如果你进行了2.3就没有必要进行2.2。当然,如果你的项目中不包含自定义的模块,仅仅包含图片资源的话,只需要进行这一步就行了。但是相比于2.3而言,2.2就显得复杂一些。

具体思路:(将.png图片转换成.py文件,使用的使用,从.py文件中读取数据并以.png的格式进行保存)

(1)将.png或者.jpg格式的图片转换成.py文件(在.py文件中,图片是以base64编码格式进行存储的)

# -*- coding: utf-8 -*-
# @Time    : 2018/6/6 18:29
# @Author  : Octan3
# @Email   : [email protected]
# @File    : Pic2py.py
# @Software: PyCharm
 
import base64
 
def pic2py(picture_name):
    """
    将图像文件转换为py文件
    :param picture_name:
    :return:
    """
    open_pic = open("%s" % picture_name, 'rb')
    b64str = base64.b64encode(open_pic.read())
    open_pic.close()
    # 注意这边b64str一定要加上.decode()
    write_data = 'img = "%s"' % b64str.decode()
    f = open('%s.py' % picture_name.replace('.png', '_png'), 'w+')
    f.write(write_data)
    f.close()
 
if __name__ == '__main__':
    # 其中pics列表中保存的图片的地址,你可以使用相对地址或者绝地地址
    pics = ["one.png", "two.png", "com.png", "socket.png", "win.png"]
    for i in pics:
        pic2py(i)
    print("ok")

运行之后,你会发现在当前的图片所在的文件夹下会新增几个_png.py文件。如果你想使用图片,你只需要导入这个模块就行

(2)在程序中引用

from one_png import img as one    #引入img变量,赋别名为one
...
# 将.py文件中的数据读取出来并以.png的格式进行保存
tmp = open('one.png', 'wb')        #创建临时的文件
tmp.write(base64.b64decode(one))    ##把这个one图片解码出来,写入文件中去。
tmp.close()                
#现在就能用了,用完(加载到程序里之后)删了就好
 
#xxxxxx     #这里one.png 就已经拿出来了,可以用来。下面就可以对此进行你想要的操作了。
do_something_with(one.png)        #做任何你想做的
#xxxxxx    
 
 
os.remove('one.png')    #用完可以删除这个临时图片

参考资料:https://blog.csdn.net/Monster_li57/article/details/80601050

 

2.3 封装的exe文件无法使用自定义的模块

为了方便维护,我将yolo检测模型单独放置在一个package下了,但是封装之后的exe程序并不能够加载自定义的模块,所有才会导致运行exe文件的时候,无法进行目标检测。所以我们需要做的就是告诉我们自定义的模块在哪个位置。

(1) 先生成.spec文件

语法:使用 pyi-makespec yourScript.py 得到yourScript.spect文件

# 在我这边是这样的(注意.dll后面是有;.的)

pyi-makespec -F -w --add-binary C:\Users\cumt\AppData\Local\Programs\Python\Python37\Lib\site-packages\cv2\opencv_videoio_ffmpeg412_64.dll;. login.py

其中:-F表示只显示一个文件夹;-w表示运行程序的时候不显示命令行;--add-binary表示要加载的动态链接库(因为我需要调用opencv读取视频,因此需要添加这个动态链接库。这个动态链接库的地址是(注意):\Lib\site-packages\cv2\opencv_ffmpeg320_64.dll)

(2)对.spec文件进行修改

主要有两个方面:一方面要将所有的.py文件加载到第一个列表中;另一个方面将你用到的所有的资源文件都加载到datas中(这个方法和2.3的重复了)

PySide2开发“安全帽检测”界面遇到的问题及其解决方案_第1张图片

(2)然后运行.spec文件生成.exe文件,注意这边即使使用参数也没有效果(需要在第一步中使用参数)

pyinstaller yourScript.spec

参考资料:

[1] https://blog.csdn.net/djshichaoren/article/details/79801531

[2] https://www.cnblogs.com/guyuyun/p/11074424.html

 

2.4 使用pyinstaller封装的exe文件无法调用opencv进行视频的读取

这是因为运行exe文件的时候,我们缺少opencv读取视频的动态链接库,我们要做的就是在封装的时候,需要将这个动态链接库复制到exe所在的文件夹下就可以了。

添加动态链接库有两种方法:

(1)第一种方法:在2.3的第二部分a列表的binaries列表中添加相关元组。我的动态链接库的地址是"C:\Users\cumt\AppData\
Local\Programs\Python\Python37\Lib\site-packages\cv2\opencv_videoio_ffmpeg412_64.dll"

因此,你需要在binaries列表中添加动态链接库的地址,这时候就变成了binaries = [("C:\\Users\\cumt\\AppData\\
Local\\Programs\\Python\\Python37\\Lib\\site-packages\\cv2\\opencv_videoio_ffmpeg412_64.dll",'.')]

(2) 第二种方式:参考2.3的第一部分,使用--add-binary 来进行添加

 

参考资料:https://stackoverflow.com/questions/44415424/videocapture-opencv-python-pyinstaller-not-opening

 

3 封装tensorflow-gpu的时候出错

在使用pyinstaller封装tensorflow-gpu 14.0的时候,报错:No such file or directory: ‘C:\Users\qhcsu\AppData\Local\Temp\_MEI106802\xxxxx\yyyy错误,我是参考了这位大哥的博客。具体做法就是:

(1)在封装目录下新建一个hook文件夹

(2)在hook文件夹中创建一个hook-xxxx的.py文件,然后在hook-xxxx.py文件中写入如下内容:

from PyInstaller.utils.hooks import collect_data_files

datas = collect_data_files("astor")

(3)因为我首先是生成了.spec文件,然后需要在.spec文件的Analysis的datas变量中加上下面这句话,一定是绝对路径

(4)然后修改Analysis下hookspath的内容,将其改成下面内容:

hookspath=['hook']

 

你可能感兴趣的:(目标检测)