Emmanuel
Emmanuel

Reputation: 333

How to correctly convert a resized image from opencv to pyqt5?

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.

Upvotes: 1

Views: 442

Answers (1)

Nighthawk
Nighthawk

Reputation: 41

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

Upvotes: 0

Related Questions