1

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.

1 Answer 1

4

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())
Sign up to request clarification or add additional context in comments.

1 Comment

"instead of an arbitrary dimension." this feels like someone else had this question because I doubt you would have thought that would work.

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.