Pyinstaller打包YOLO8项目GUI界面多次出现以及控制台闪烁问题解决方案

项目大致情况

  • 一个利用 YOLO8 进行内容检测的项目,项目中除了通过 YOLO8 实现的内容检测部分,还有通过 PyQt5 实现的 GUI
  • 项目使用 Pyinstaller 进行打包
  • 所使用的环境:
    • Windows 10
    • PyCharm
    • Anaconda
    • Python 3.10
    • Ultralytics 8.1.29
    • PyQt5 5.15.10
    • Pyinstaller 5.13.2

所遇到的问题与解决方案

阶段一:报错找不到模块或资源

初次尝试使用 Pyinstaller 进行打包:

pyinstaler -w main.py

此处 main.py 是进行打包的文件名,尝试运行打包出来的 exe,报错找不到文件之类的,解决办法:

  1. 模块未被打包: 由于 Ultralytics 包比较新,Pyinstaller 还没有包含自动将这个模块打包进去的指令,所以需要到你的这个虚拟环境目录下找到 Ultralytics 文件夹,将其复制到你的 python 项目目录下,然后使用 pyinstaller 打包指令时添加上额外的数据打包指令,即使用:
    pyinstaller --collect-data ultralytics -w main.py
    
    进行打包
  2. 额外资源未被打包: 由于使用了自己的 .bt 文件模型以及其他许多图片资源素材,这些内容也需要被打包进去,不然 exe 运行时找不到这些内容就会报错,因此可以在打包时先使用:
    pyi-makespec --collect-data ultralytics -w main.py
    
    生成 .spec 文件,在这个文件中的
    # -*- mode: python ; coding: utf-8 -*-
    from PyInstaller.utils.hooks import collect_data_files
    
    datas = [('res', '.')]
    datas += collect_data_files('ultralytics')
    
    第一个 datas 这里进行修改,默认生成的是 datas = [] , 你需要在里面添加上你的资源所在的文件夹,便于打包,例如我在项目目录下添加了 res 这个文件夹,并且把所有的资源都放进去,所以最终就是 datas = [('res', '.')],这样你的这些额外资源就也会被打包进行,理论上你的 exe 就能正常工作了,但是!总是会有意外发生!这就是阶段二的内容了

阶段二:exe 运行奇怪报错

正常来说,你把所有这些 python 模块和资源文件都打包好了,就能正常运行了,但是这边由于用了 -w 这个不显示控制台的打包方法,如果你的程序代码里包含了 print() 或者 LOGGER 之类的打印信息,那么这里就会由于找不到控制台报一些奇怪的错误

如果是 print() 这个很好处理,你自己写的都注释掉就行,但是这边用了 UltralyticsYOLO 模块,它 __init__LOGGER 这个东西,在好几个文件里都有涉及,用来输出一些 YOLO 的过程信息,所以你需要找到 Ultralytics 文件夹里 utils 文件夹下的 __init__.py 在大概 270 行左右:

# Set logger
# LOGGER = set_logging(LOGGING_NAME, verbose=VERBOSE)  # define globally (used in train.py, val.py, predict.py, etc.)
LOGGER = None

LOGGER 设为 None,这样之后就不会输出东西了,然后也要找到调用了 LOGGER 的几个文件,基本上是 Ultralytics 文件夹里 engine 文件夹下的几个文件,可以搜索一下然后把所有 LOGGER 都注释了,如果在单行的条件语句里记得补充上 pass 不然会报错

这样把所有的涉及输出到控制台的东西都处理好了,再次重新打包,你的 exe 应该就可以正确运行了,但是!还是会有问题,不然就不会有这篇记录了!

阶段三:启动涉及 YOLO预测功能时,控制台会闪烁出现,同时生成了额外的GUI界面

这部分问题是最摸不着头脑的,想到了估计会是多线程的问题,但是不知道具体问题在哪里。最后在 Ultralytics 以及 PyinstallerGitHub 问题讨论区留下问题,得到了大佬的解答才得以解决:

  1. 添加控制多线程的内容: 需要在主程序运行自己的 PyQt 界面的代码执行前,先添加额外的一个:

    if __name__ == '__main__':
    	import multiprocessing
    	multiprocessing.freeze_support()
    	
    	app = QApplication(sys.argv)
    	window = Window()
    	window.show()
    	sys.exit(app.exec_())
    

    应该是通过 multiprocessing.freeze_support() 避免多线程的一些问题,这样就能避免生成额外的 GUI 界面

  2. 修改YOLO中存在线程问题的代码: 找到 Ultralytics 文件夹里 utils 文件夹下的 torch_utils.py 文件,里面的 73 行左右,这边在获取机器的 cpu 信息的时候应该是有启动了多线程之类的东西:

    def get_cpu_info():
    	# """Return a string with system CPU information, i.e. 'Apple M2'."""
    	# import cpuinfo  # pip install py-cpuinfo
    	#
    	# k = "brand_raw", "hardware_raw", "arch_string_raw"  # info keys sorted by preference (not all keys always available)
    	# info = cpuinfo.get_cpu_info()  # info dict
    	# string = info.get(k[0] if k[0] in info else k[1] if k[1] in info else k[2], "unknown")
    	import wmi
    
    	c = wmi.WMI()
     	string = c.Win32_Processor()[0].Name
    	return string.replace("(R)", "").replace("CPU ", "").replace("@ ", "")
    

    问题应该出在 cpuinfo 这个模块估计会启动其他线程造成一些冲突之类,导致了这个控制台在启动 YOLO 的检测时会突然出现闪烁的问题,所以我修改成通过 wmi 模块简单拿到 cpu 型号信息这样一个简单方法去规避了问题。

    如果你是按我这样在修改完 73 行附近这个函数,那应该基本所有问题就都解决了,如果你这边的 cpu 信息获取方法调整的和我不一样,注意 164 行左右的调用也需要调整,或者你也可以直接在那边写死 cpu 信息,不过这样程序运行平台就很局限了

总结

把以上三个阶段的内容都修改完,你的 YOLO 以及 PyQt5 项目应该就能被 Pyinstaller 完美打包好,并且正常的运行了!如果你还希望修改一下 exe 的 icon,记得先在 pyqt 的窗口类里面加上 icon的信息,同时在打包的时候指定上 icon文件:

from PyQt5.QtGui import QIcon

self.setWindowIcon(QIcon('res/your_icon.ico'))
pyi-makespec --collect-data ultralytics -F -w -i your.ico main.py
pyinstaller main.spec

这样最终可以打包出来一个单独的 exe 文件,并且是你的icon,最后附上一个 pngico 文件的代码:

from PIL import Image


def make_icon():
    # 读取PNG图像
    png_image = Image.open('icon.png')

    # 定义要生成的ICO文件中的各个尺寸
    sizes = [(16, 16), (32, 32), (48, 48), (64, 64), (256, 256)]

    # 创建ICO文件
    ico_image = Image.new('RGBA', (max(sizes)), (255, 255, 255, 0))

    # 将PNG图像按照不同尺寸复制到ICO文件中
    for size in sizes:
        resized_image = png_image.resize(size, Image.ANTIALIAS)
        ico_image.paste(resized_image, (0, 0))

    # 保存ICO文件
    ico_image.save('output.ico')


if __name__ == '__main__':
    make_icon()

写在最后

第一次做 YOLO 以及 PyQt5 并且最后用 Pyinstaller 来打包,遇到这些找了很多内容,最后在 GitHub 大佬帮助下终于完美解决,记录一下,希望能帮助到同样遇到类似问题的朋友!

  • 17
    点赞
  • 34
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值