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
相关产品推荐
相关产品推荐

