简体   繁体   中英

How to display Python OpenCV image (Numpy array) with PyQt6?

I'm trying to display an OpenCV image (NumPy array) using PyQt6. Previous Qt versions (PyQt4 and PyQt5) convert the NumPy array to a QPixmap then display it using a QLabel but this doesn't seem to work in PyQt6 .

Input image

When I try to display it, I get a distorted image

Code

from PyQt6.QtWidgets import QApplication, QWidget, QMainWindow, QLabel, QHBoxLayout, QGridLayout
from PyQt6.QtGui import QImage, QPixmap 
import sys
import cv2

class DisplayImageWidget(QWidget):
    def __init__(self):
        super(DisplayImageWidget, self).__init__()
        self.image = cv2.imread('2.png')
        self.convert = QImage(self.image, 400, 400, QImage.Format.Format_BGR888)
        self.frame = QLabel()
        self.frame.setPixmap(QPixmap.fromImage(self.convert))
        
        self.layout = QHBoxLayout(self)
        self.layout.addWidget(self.frame)

if __name__ == '__main__':
    app = QApplication([])
    
    main_window = QMainWindow()
    main_window.setWindowTitle('Video Frame Acquisition')
    main_window.setFixedSize(500, 500)
    
    central_widget = QWidget()
    main_layout = QGridLayout()
    central_widget.setLayout(main_layout)
    main_window.setCentralWidget(central_widget)
    
    display_image_widget = DisplayImageWidget()
    main_layout.addWidget(display_image_widget, 0, 0)
   
    main_window.show()
    sys.exit(app.exec())

I experimented with all types of different QtGui.QImage.Format types but I'm pretty sure the Format_BGR888 should be correct. The image is showing but I can't get it to format correctly.

Okay I found the solution. It turns out you have to pass the image's width and height instead of an arbitrary dimension.

self.convert = QImage(self.image, self.image.shape[1], self.image.shape[0], QImage.Format.Format_BGR888)

That fixed the random pixels but it was still slanted for some reason.

After some digging, it turns out there's a not well documented bytesPerLine (stride) parameter in QImage . The fix was adding strides[0] . The parameter specifies the number of bytes required by the image pixels in a given row. But I'm still not sure what it does exactly since with other images, the picture displays properly without adding the additional parameter.

self.convert = QImage(self.image, self.image.shape[1], self.image.shape[0], self.image.strides[0], QImage.Format.Format_BGR888)

Full working code

from PyQt6.QtWidgets import QApplication, QWidget, QMainWindow, QLabel, QHBoxLayout, QGridLayout
from PyQt6.QtGui import QImage, QPixmap 
import sys
import cv2

class DisplayImageWidget(QWidget):
    def __init__(self):
        super(DisplayImageWidget, self).__init__()
        self.image = cv2.imread('2.png')
        self.convert = QImage(self.image, self.image.shape[1], self.image.shape[0], self.image.strides[0], QImage.Format.Format_BGR888)
        self.frame = QLabel()
        self.frame.setPixmap(QPixmap.fromImage(self.convert))
        
        self.layout = QHBoxLayout(self)
        self.layout.addWidget(self.frame)

if __name__ == '__main__':
    app = QApplication([])
    
    main_window = QMainWindow()
    main_window.setWindowTitle('Video Frame Acquisition')
    main_window.setFixedSize(500, 500)
    
    central_widget = QWidget()
    main_layout = QGridLayout()
    central_widget.setLayout(main_layout)
    main_window.setCentralWidget(central_widget)
    
    display_image_widget = DisplayImageWidget()
    main_layout.addWidget(display_image_widget, 0, 0)
   
    main_window.show()
    sys.exit(app.exec())

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM