Od Numpy do awarii QImage

Aug 29 2020

Poniższy kod ulega awarii po kliknięciu przycisku lub po kilku kliknięciach, gdy sygnał jest emitowany z wątku i przechwytywany w głównym interfejsie użytkownika.

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

Program nie zawiesza się, jeśli wysokość i szerokość są ustawione na małą liczbę, powiedzmy 3. Program również nie zawiesza się, jeśli przed emisją przekonwertujemy qimage do qpixmap i zmienimy typ sygnału na QPixmap. Program został pierwotnie napisany w celu pobierania obrazów z kamery internetowej przy użyciu opencv. Tablica numpy utworzona przez opencv również ulega awarii w przypadku dużych rozmiarów obrazów.

Używany system operacyjny to Windows10, wersja pyqt to 5.12.3

Masz jakiś pomysł, co może być przyczyną katastrofy?

Odpowiedzi

1 eyllanesc Aug 29 2020 at 12:13

W Linuksie z PyQt5 5.15 nie odtwarzam problemu, ale ten błąd jest powszechny i ​​pojawia się, ponieważ przekazywanie "danych" nie powoduje kopiowania informacji, ale dane są udostępniane, więc w pewnym momencie cv_img i wszystkie powiązane obiekty są niszczone, w tym " data ”, więc podczas przesyłania ich przez sygnał i ustawiania w QLabel,„ dane ”są odczytywane, ale nie mają już zarezerwowanej pamięci. Rozwiązaniem w tym przypadku jest skopiowanie „danych”:

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

lub skopiuj QImage.

return qim.copy()