1

Goal:

I am trying to display an image using PyQt5 edited using OpenCV.

Annoying bug while converting image

The above image shows a side by side comparison of my expected output (Shown from a openCV Window) and the actual output displayed in a PyQt5 Label Pixmap.

The picture shows that the image is successfully resized, but not being displayed correctly.

About the QLabel (used to display the image):

The QLabel is within a Frame. Here's how it is defined:

        self.ImageDisplayerLB = QtWidgets.QLabel(self.topFrame) #topFrame is a QFrame
        self.ImageDisplayerLB.setEnabled(True)
        self.ImageDisplayerLB.setText("")
        self.ImageDisplayerLB.setPixmap(QtGui.QPixmap("./<image>.jpg"))
        self.ImageDisplayerLB.setAlignment(QtCore.Qt.AlignCenter)
        self.ImageDisplayerLB.setObjectName("ImageDisplayerLB")
        self.gridLayout_2.addWidget(self.ImageDisplayerLB, 0, 0, 1, 1)

About the QFrame used in QLabel:

The QFrame does have a minimum height and width (Size) set so it doesn't look too small while displaying the image.

        self.topFrame = QtWidgets.QFrame(self.frame)
        self.topFrame.setMinimumSize(QtCore.QSize(831, 409))
        self.topFrame.setStyleSheet("background-color: rgb(1,1,1);")
        self.topFrame.setObjectName("topFrame")
        self.topFrame.setFrameShape(QtWidgets.QFrame.StyledPanel)
        self.topFrame.setFrameShadow(QtWidgets.QFrame.Raised)

The Pixmap is being set again with a different function call while handling an event. The below code snippet is where the error seem to be occuring.

        if hw := self.__check_oversized_image(image): # Returns height, width if image is larger than the QLabel size, else returns None.
            w, h = self.ImageDisplayerLB.width(), self.ImageDisplayerLB.height()
            self.ImageDisplayerLB.pixmap().detach() # Tried the same without it, makes no difference

            thresh = min((self.ImageDisplayerLB.width(), self.ImageDisplayerLB.height()))

            r = thresh / image.shape[1]
            dim = (thresh, int(image.shape[0] * r))
            image = cv2.resize(image, dim, interpolation=cv2.INTER_AREA) # Resize Image maintaining the ratio

            self.ImageDisplayerLB.setScaledContents(True) # Makes no difference with or without this

# End of if block
        frame = cv2.cvtColor(image, cv2.COLOR_BGR2RGB)
        self.qimage = QtGui.QImage(
            frame, frame.shape[1], frame.shape[0], QtGui.QImage.Format_RGB888
        )
        try:
            self.pimage = QtGui.QPixmap.fromImage(self.qimage, QtCore.Qt.AutoColor)
            self.ImageDisplayerLB.setPixmap(self.pimage)
        except Exception as e:
            print(e)

When does this occur?

This issue is only when the image is found oversized and I am resizing the image. It works fine without the image being oversized.

Any help to fix the issue where the image is grayscale and tilted.

Minimal Reproducible Code:

from PyQt5 import QtWidgets, QtCore, QtGui
from PyQt5.QtWidgets import QMainWindow
import cv2 as cv


class main(QMainWindow):
    def __init__(self):
        super().__init__()
        self.mainFrame = QtWidgets.QFrame(self)
        self.grid = QtWidgets.QGridLayout(self.mainFrame)
        self.label = QtWidgets.QLabel(self.mainFrame)
        img = cv.imread("cert_template.png")  # Image from -> https://simplecert.net/certificate-templates/
        frame = img.copy()
        self.label.setPixmap(QtGui.QPixmap.fromImage(QtGui.QImage(frame.data,frame.shape[1],frame.shape[0],QtGui.QImage.Format_RGB888,))) # Not sure why this is grayscale and tilted
        self.mainFrame.setMinimumSize(QtCore.QSize(831, 409))
        self.label.setScaledContents(True)
        self.grid.addWidget(self.label, 0, 0, 1, 1)
        cv.imshow("image", img) # Displays the predicted output
        cv.waitKey(0) 


if __name__ == "__main__":
    app = QtWidgets.QApplication([])
    window = main()
    window.show()
    app.exec_()

Much appreciated.

Emmanuel
  • 156
  • 6
  • 1
    The frame and qlabel setup is irrelevant, the problem is clearly in the cv resizing and recreation of the image. Try with `self.qimage = QtGui.QImage(frame, frame.shape[1], frame.shape[0], frame.step, QtGui.QImage.Format_RGB888)`. If it still doesn't work, please provide a [mre]. – musicamante Jun 16 '22 at 19:24
  • @musicamante Hello! QFrame and QLabel setup seemed relevent because I thought, the "minimum height" constraint was something necessary to point out. I'll provide a minimal reproducible example very soon as using `frame.step` didn't work. – Emmanuel Jun 20 '22 at 15:31

1 Answers1

0

Pass bytesPerLine as 3 * img.shape[1] in the QImage constructor:

QtGui.QImage(img, img.shape[1], img.shape[0], 3 * img.shape[1], QtGui.QImage.Format_BGR888)

Working minimal example:

from PyQt5 import QtWidgets, QtGui
from PyQt5.QtWidgets import QWidget, QLabel
import cv2


class MainWindow(QWidget):
    def __init__(self):
        super().__init__()

        img = cv2.imread("cert_template.png")

        qt5_img = QtGui.QImage(img, img.shape[1], img.shape[0], 3 * img.shape[1], QtGui.QImage.Format_BGR888)
        pixmap = QtGui.QPixmap.fromImage(qt5_img)

        self.image_label = QLabel(self)
        self.image_label.setPixmap(pixmap)

        self.showMaximized()


if __name__ == "__main__":
    app = QtWidgets.QApplication([])
    window = MainWindow()
    app.exec_()
Nighthawk
  • 41
  • 5