Numpy 到 QImage 崩溃

Numpy to QImage Crashing

当信号从线程发出并在主 gui 中捕获时,以下代码在单击按钮时或单击几下后崩溃。

from PyQt5 import QtCore, QtGui, QtWidgets
from PyQt5.QtGui import QPixmap, QImage
from PyQt5.QtCore import pyqtSignal, pyqtSlot, Qt, QThread
import numpy as np
import time
from PyQt5.QtWidgets import QApplication, QDialog, QPushButton, QVBoxLayout


def convert_np_qimage(cv_img , width, height):
    h, w, ch = cv_img.shape
    bytes_per_line = ch * w
    qim = QtGui.QImage(cv_img.data, w, h, bytes_per_line, QtGui.QImage.Format_RGB888)
    print(qim.size())
    return qim

class VideoThread(QThread):
    change_qimage_signal = pyqtSignal(QImage)
    
    def __init__(self):
        super().__init__()

    def run(self):
        print("run")
        width = 1280
        height = 1280
        
        cv_img = np.zeros([height,width,3],dtype=np.uint8)
        cv_img.fill(255)
        print("image shape: ", cv_img.shape)

        qimg = convert_np_qimage(cv_img, width, height)
        self.change_qimage_signal.emit(qimg)
        print("emitted")

        
    def stop(self):
        self.wait()

import sys

class Dialog(QDialog):

    def __init__(self):
        super(Dialog, self).__init__()
        Dialog.resize(self, 640, 480)
        
        button=QPushButton("Click")
        button.clicked.connect(self.startThread)
        
        mainLayout = QVBoxLayout()
        mainLayout.addWidget(button)
        
        
        self.setLayout(mainLayout)
        self.setWindowTitle("QImage Example")
        
    def startThread(self):
        self.thread = VideoThread()
        self.thread.change_qimage_signal.connect(self.getPixmap)
        self.thread.start()      
        
    def getPixmap(self, qimg):
        print("got qimage")
        qpixmap = QPixmap.fromImage(qimg)
        

if __name__ == '__main__':
    app = QApplication(sys.argv)
    dialog = Dialog()
    sys.exit(dialog.exec_())

如果将高度和宽度设置为较小的数字(例如 3),程序不会崩溃。 如果我们在发射之前将 qimage 转换为 qpixmap 并更改信号,程序也不会崩溃 键入 QPixmap。 该程序最初是为使用 opencv 从网络摄像头获取图像而编写的。创建的 numpy 数组 对于大图像尺寸,opencv 也会崩溃。

使用的OS是Windows10,pyqt版本是5.12.3

知道崩溃的原因是什么吗?

在 PyQt5 5.15 的 Linux 中,我没有重现该问题,但该错误很常见并且发生是因为传递“数据”不会复制信息而是共享数据,所以在某些时候 cv_img 并且所有关联的对象都被销毁,包括“数据”,因此当通过信号传输它并将其设置在 QLabel 中时,“数据”被读取但它不再保留内存。这种情况下的解决方案是复制“数据”:

qim = QtGui.QImage(
    cv_img.data.tobytes(), w, h, bytes_per_line, QtGui.QImage.Format_RGB888
)

或者复制QImage。

return qim.copy()