Numpy para QImage quebrando

Aug 29 2020

O código a seguir trava ao clicar no botão ou após alguns cliques quando o sinal é emitido do thread e capturado na interface principal.

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

O programa não trava se a altura e a largura são configuradas para um número pequeno, digamos 3. O programa também não trava se convertermos qimage em qpixmap antes de emitir e alterar o tipo de sinal para QPixmap. O programa foi originalmente escrito para obter imagens da webcam usando opencv. A matriz numpy criada por opencv também falha para tamanhos de imagem grandes.

O sistema operacional usado é o Windows10, a versão pyqt é 5.12.3

Alguma ideia de qual pode ser o motivo do acidente?

Respostas

1 eyllanesc Aug 29 2020 at 12:13

No Linux com PyQt5 5.15 não reproduzo o problema, mas esse erro é comum e ocorre porque a passagem de "dados" não copia as informações, mas sim os dados são compartilhados, então em algum ponto cv_img e todos os objetos associados são destruídos incluindo para " dados "então ao transmiti-lo através do sinal e configurá-lo no QLabel que" dados "são lidos, mas não tem mais memória reservada. A solução neste caso é copiar "dados":

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

ou copie o QImage.

return qim.copy()