Reputation: 46680
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.
Upvotes: 0
Views: 2484
Reputation: 46680
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())
Upvotes: 3