Crash de Numpy à QImage

Aug 29 2020

Le code suivant plante en cliquant sur le bouton ou après quelques clics lorsque le signal est émis par le thread et pris dans l'interface graphique principale.

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_())

Le programme ne plante pas si la hauteur et la largeur sont définies sur un petit nombre, disons 3. Le programme ne plante pas non plus si nous convertissons qimage en qpixmap avant d'émettre et changeons le type de signal en QPixmap. Le programme a été initialement écrit pour obtenir des images de la webcam en utilisant opencv. Le tableau numpy créé par opencv plante également pour les grandes tailles d'image.

Le système d'exploitation utilisé est Windows10, la version pyqt est 5.12.3

Une idée de la raison de l'accident?

Réponses

1 eyllanesc Aug 29 2020 at 12:13

Sous Linux avec PyQt5 5.15, je ne reproduis pas le problème, mais cette erreur est courante et se produit parce que le passage de "données" ne copie pas les informations mais plutôt les données sont partagées, donc à un moment donné, cv_img et tous les objets associés sont détruits, y compris à " data "donc lors de sa transmission via le signal et de sa mise en place dans le QLabel," data "est lu mais il n'a plus de mémoire réservée. La solution dans ce cas est de copier des "données":

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

ou copiez le QImage.

return qim.copy()