You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

Numpy数组转PIL Image再转Qt QImage/QPixmap时部分图片致GUI崩溃求助

问题描述

处理部分图片时程序运行正常,但处理另一类图片时,终端无报错信息,但GUI会直接无响应并关闭。

相关代码文件

minimal.py (GUI定义)

from PyQt5 import QtCore, QtGui, QtWidgets

class Ui_MainWindow(object):
    def setupUi(self, MainWindow):
        MainWindow.setObjectName("MainWindow")
        MainWindow.resize(457, 353)
        self.centralwidget = QtWidgets.QWidget(MainWindow)
        self.centralwidget.setObjectName("centralwidget")
        self.display = QtWidgets.QLabel(self.centralwidget)
        self.display.setGeometry(QtCore.QRect(80, 20, 271, 261))
        self.display.setScaledContents(True)
        self.display.setObjectName("display")
        self.upload = QtWidgets.QPushButton(self.centralwidget)
        self.upload.setGeometry(QtCore.QRect(80, 300, 71, 21))
        self.upload.setObjectName("upload")
        self.translate = QtWidgets.QPushButton(self.centralwidget)
        self.translate.setGeometry(QtCore.QRect(230, 300, 71, 21))
        self.translate.setObjectName("translate")
        MainWindow.setCentralWidget(self.centralwidget)
        self.statusbar = QtWidgets.QStatusBar(MainWindow)
        self.statusbar.setObjectName("statusbar")
        MainWindow.setStatusBar(self.statusbar)

        self.retranslateUi(MainWindow)
        QtCore.QMetaObject.connectSlotsByName(MainWindow)

    def retranslateUi(self, MainWindow):
        _translate = QtCore.QCoreApplication.translate
        MainWindow.setWindowTitle(_translate("MainWindow", "MainWindow"))
        self.display.setText(_translate("MainWindow", "TextLabel"))
        self.upload.setText(_translate("MainWindow", "upload"))
        self.translate.setText(_translate("MainWindow", "modify"))

main.py (业务逻辑)

from PyQt5 import QtCore
from PyQt5.QtWidgets import QFileDialog
from PyQt5.QtGui import QPixmap, QImage
from PyQt5 import QtWidgets
from PyQt5.QtCore import QThread, pyqtSignal
from minimal import Ui_MainWindow
from cvPic import Edit
class Interaction(QtWidgets.QMainWindow, Ui_MainWindow):
    def __init__(self):
        super(Interaction, self).__init__()
        self.setupUi(self)
        self.index = 0
        self.newIndex = 0
        self.translatedFiles = []
        self.files = []
        self.isClicked = False
        self.upload.clicked.connect(self.upload1)
        self.upload.clicked.connect(self.change1)
        self.translate.clicked.connect(self.translate1)
        self.translate.clicked.connect(self.change)

    def showImage(self):
        if self.isClicked == True:
            im = self.translatedFiles[self.newIndex]
            qim = QImage(im[0], im[1].size[0], im[1].size[1], QImage.Format_RGB888)
            pix = QPixmap(qim)
            self.display.setPixmap(pix)
        else:
            pix = QPixmap(self.files[self.index])
            self.display.setPixmap(pix)
            print(self.files[self.index])

    def upload1(self):
        filenames, _ =  QFileDialog.getOpenFileNames(
            None,
            "QFileDialog.getOpenFileNames()",
            "",
            "Image files (*.jpg *.png)"
        )
        for file in filenames:
            self.files.append(file)
        self.showImage()
    
    def change(self):
        self.isClicked = True

    def change1(self):
        self.isClicked = False

    def afterThread(self, s):
        self.translatedFiles.append(s)
        QtCore.QTimer.singleShot(0, self.showImage)
        print("THREAD COMPLETE!")

    def translate1(self):
        self.thread = QThread()
        self.worker = Edit(self.files[self.index])
        self.worker.moveToThread(self.thread)
        self.thread.started.connect(self.worker.run)
        self.worker.result.connect(self.afterThread)
        self.worker.finished.connect(self.worker.deleteLater)
        self.thread.finished.connect(self.thread.deleteLater)
        self.worker.finished.connect(self.thread.quit)
        self.thread.start()


if __name__ == '__main__':
    import sys
    app = QtWidgets.QApplication(sys.argv)
    w = Interaction()
    w.show()
    sys.exit(app.exec())

cvPic.py (图像处理逻辑)

import cv2
from PIL import Image
from PyQt5.QtCore import QTimer, QRunnable, pyqtSignal, QObject, QThread
import numpy as np

class Edit(QObject):
    result = pyqtSignal(object)
    finished = pyqtSignal()

    def __init__(self, img):
        super(Edit, self).__init__()
        self.img1 = cv2.imread(img)
        self.image = cv2.cvtColor(self.img1, cv2.COLOR_BGR2RGB)

    def mod(self, img):
        gray = cv2.cvtColor(img, cv2.COLOR_BGR2GRAY)
        blur = cv2.GaussianBlur(gray, (5, 5),cv2.BORDER_DEFAULT)
        cv2.rectangle(blur, (0, 0), (250, 250), (0, 255,0), thickness=cv2.FILLED)
        cv2.line(blur, (0, 0), (250, 250), (255, 255,0), thickness=3)
        cv2.putText(blur, "HELLO WORLD!", (225, 225), cv2.FONT_HERSHEY_TRIPLEX, 1.0, (255, 255, 255), 2)
        myarray = np.array(blur)
        img = Image.fromarray(myarray)
        im = img.convert("RGB")
        data = im.tobytes("raw","RGB")
        self.result.emit((data, im))
        self.finished.emit()
    
    def run(self):
        self.mod(self.image)

问题排查与修复方案

1. 核心崩溃原因:固定坐标超出图片尺寸

cvPic.py的mod方法中,你使用了固定的(250,250)作为图形绘制坐标,但如果处理的图片宽/高小于250像素,OpenCV会触发内存越界错误——这类错误不会在终端输出明确报错,但会直接终止程序进程,表现为GUI无响应并关闭。

2. 次要问题:线程连接顺序与错误处理缺失

  • 线程信号连接顺序错误,可能导致资源无法正确释放
  • 未处理图片读取失败的情况,若cv2.imread返回None,后续操作会直接崩溃

修复后的代码

修复cvPic.py

import cv2
from PIL import Image
from PyQt5.QtCore import pyqtSignal, QObject
import numpy as np

class Edit(QObject):
    result = pyqtSignal(object)
    finished = pyqtSignal()
    error = pyqtSignal(str)  # 添加错误信号,传递报错信息

    def __init__(self, img):
        super(Edit, self).__init__()
        self.img_path = img
        # 先检查图片是否读取成功
        self.img1 = cv2.imread(img)
        if self.img1 is None:
            self.error.emit(f"无法读取图片: {img}")
            self.finished.emit()
            return
        self.image = cv2.cvtColor(self.img1, cv2.COLOR_BGR2RGB)
        self.img_height, self.img_width = self.img1.shape[:2]

    def mod(self):
        if self.img1 is None:
            return
        # 修正颜色空间转换:self.image是RGB格式,需用RGB2GRAY
        gray = cv2.cvtColor(self.image, cv2.COLOR_RGB2GRAY)
        blur = cv2.GaussianBlur(gray, (5, 5), cv2.BORDER_DEFAULT)
        
        # 根据图片尺寸动态计算坐标,避免越界
        max_size = min(self.img_width, self.img_height)
        end_x = min(250, max_size)
        end_y = min(250, max_size)
        # 预留文字空间,避免超出图片边界
        text_x = min(225, max_size - 100)
        text_y = min(225, max_size - 20)

        cv2.rectangle(blur, (0, 0), (end_x, end_y), (0, 255, 0), thickness=cv2.FILLED)
        cv2.line(blur, (0, 0), (end_x, end_y), (255, 255, 0), thickness=3)
        cv2.putText(blur, "HELLO WORLD!", (text_x, text_y), cv2.FONT_HERSHEY_TRIPLEX, 1.0, (255, 255, 255), 2)
        
        myarray = np.array(blur)
        img = Image.fromarray(myarray)
        im = img.convert("RGB")
        data = im.tobytes("raw", "RGB")
        self.result.emit((data, im))
        self.finished.emit()
    
    def run(self):
        self.mod()

修复main.py部分逻辑

# ... 保留原有代码,仅修改以下部分 ...
    def translate1(self):
        self.thread = QThread()
        self.worker = Edit(self.files[self.index])
        self.worker.moveToThread(self.thread)
        # 调整信号连接顺序,先quit再释放资源
        self.thread.started.connect(self.worker.run)
        self.worker.result.connect(self.afterThread)
        self.worker.error.connect(self.show_error)  # 添加错误弹窗提示
        self.worker.finished.connect(self.thread.quit)
        self.worker.finished.connect(self.worker.deleteLater)
        self.thread.finished.connect(self.thread.deleteLater)
        self.thread.start()
    
    def show_error(self, msg):
        QtWidgets.QMessageBox.critical(self, "错误", msg)

    def showImage(self):
        # 添加索引越界检查
        if self.isClicked == True:
            if 0 <= self.newIndex < len(self.translatedFiles):
                im = self.translatedFiles[self.newIndex]
                qim = QImage(im[0], im[1].size[0], im[1].size[1], QImage.Format_RGB888)
                pix = QPixmap(qim)
                self.display.setPixmap(pix)
            else:
                self.display.setText("无处理后的图片")
        else:
            if 0 <= self.index < len(self.files):
                pix = QPixmap(self.files[self.index])
                self.display.setPixmap(pix)
                print(self.files[self.index])
            else:
                self.display.setText("无上传的图片")

内容的提问来源于stack exchange,提问作者Andrew Nketsiah

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.13 14:10:27