0

I am dynamically resizing image frame received from webcam via OpenCV but it shows distorted image.

Given the code and ui file below. To resize , please press hold right click and drag.

This size works: enter image description here

But this size doesn't work:
enter image description here

I have tried to make resolution in even numbers, like if resolution is 623x420 then i am converting it into 624x420. This reduces scrambling but still it occurs for some sizes. Tried adding wait keys but no effect. Tried with video files instead of webcam, the result is same.

import sys
from PyQt5 import QtWidgets, QtGui, QtCore
import cv2
from we import Ui_MainWindow

class MainWindow_exec(QtWidgets.QMainWindow, Ui_MainWindow):
    def __init__(self, parent=None):
        QtWidgets.QMainWindow.__init__(self, parent)
        self.setupUi(self)
        self.fps=24
        self.start_camera()
        self.rightClick=None
        self.x = 600
        self.y = 400

    def mousePressEvent(self, QMouseEvent):
        self.timer.stop()
        self.press = QMouseEvent.pos()

        if QMouseEvent.button() == QtCore.Qt.RightButton:
            self.rdragx = QMouseEvent.x()
            self.rdragy = QMouseEvent.y()        
            self.currentx = self.width()
            self.currenty = self.height()
            self.rightClick = True

    def mouseMoveEvent(self, event):
        if event.buttons() == QtCore.Qt.LeftButton:
            globalPos = event.globalPos()
            self.move(globalPos-self.press)

        if self.rightClick == True:
            self.x = self.currentx + event.x() - self.rdragx
            self.y = self.currenty + event.y() - self.rdragy
            self.resize(self.x, self.y)
            self.label.setFixedSize(self.x, self.y)

    def mouseReleaseEvent(self, event):
        self.rightClick = False
        self.timer.start(1000./self.fps)

    def next_frame(self):
        ret, frame = self.cap.read()
        frame = cv2.resize(frame,(self.x,self.y ))
        frame = cv2.cvtColor(frame, cv2.COLOR_BGR2RGB)
        img = QtGui.QImage(frame, frame.shape[1], frame.shape[0], QtGui.QImage.Format_RGB888)
        pix = QtGui.QPixmap.fromImage(img)
        self.label.setPixmap(pix)

    def start_camera(self):
        self.cap = cv2.VideoCapture(0)
        self.timer = QtCore.QTimer()
        self.timer.timeout.connect(self.next_frame)
        self.timer.start(1000./self.fps)


if __name__ == "__main__":
    app = QtWidgets.QApplication(sys.argv)
    MainWindow1 = MainWindow_exec()
    MainWindow1.show()
    sys.exit(app.exec_())

UI file we.py

from PyQt5 import QtCore, QtGui, QtWidgets

class Ui_MainWindow(object):
    def setupUi(self, MainWindow):
        MainWindow.setObjectName("MainWindow")
        MainWindow.resize(532, 353)
        MainWindow.setWindowOpacity(1.0)
        self.centralwidget = QtWidgets.QWidget(MainWindow)
        self.centralwidget.setObjectName("centralwidget")
        self.gridLayout = QtWidgets.QGridLayout(self.centralwidget)
        self.gridLayout.setContentsMargins(0, 0, 0, 0)
        self.gridLayout.setSpacing(0)
        self.gridLayout.setObjectName("gridLayout")
        self.label = QtWidgets.QLabel(self.centralwidget)
        self.label.setStyleSheet("background:grey")
        self.label.setObjectName("label")
        self.gridLayout.addWidget(self.label, 0, 0, 1, 1)
        MainWindow.setCentralWidget(self.centralwidget)

        QtCore.QMetaObject.connectSlotsByName(MainWindow)


if __name__ == "__main__":
    import sys
    app = QtWidgets.QApplication(sys.argv)
    MainWindow = QtWidgets.QMainWindow()
    ui = Ui_MainWindow()
    ui.setupUi(MainWindow)
    MainWindow.show()
    sys.exit(app.exec_())
Vaas
  • 69
  • 1
  • 12
  • change to `def next_frame(self):` `ret, frame = self.cap.read()` `if ret:` `frame = cv2.resize(frame, (self.x,self.y ))` `frame = cv2.cvtColor(frame, cv2.COLOR_BGR2RGB)` `h, w, ch = frame.shape` `bytesPerLine = ch * w` `qImg = QtGui.QImage(frame.data, w, h, bytesPerLine, QtGui.QImage.Format_RGB888)` `self.label.setPixmap(QtGui.QPixmap.fromImage(qImg))` – eyllanesc Apr 25 '19 at 04:42
  • I think that the issue is with the library you are using for rendering the frame and not something related to OpenCV. One suggestion I can provide you is to try converting your frames to BGRA. – ZdaR Apr 25 '19 at 04:42
  • Your conversion method is incorrect, you need to set the bytesPerLine as the third parameter, you must also use the variable ret that indicates whether the frame is valid or not. – eyllanesc Apr 25 '19 at 04:44
  • If you can not understand the code that you leave in the first comment, I have published the code formatted in the following link:: https://pastebin.com/A0Qxe95j – eyllanesc Apr 25 '19 at 04:59
  • @eyllanesc, your solution worked perfect. Adding byesPerLine parameter to QImage did the magic. Thank you so much. – Vaas Apr 25 '19 at 05:06

1 Answers1

0

The problem is that QImage expects the data rows to be 4 byte padded.

Add this to the beginning of the file

import numpy as np

def padded_row(row_size, padding=4):
    return (row_size + padding - 1) // padding * padding

Add padding to the frame before you pass it to QImage:

def next_frame(self):
    ret, frame = self.cap.read()
    frame = cv2.resize(frame,(self.x,self.y ))
    frame = cv2.cvtColor(frame, cv2.COLOR_BGR2RGB)

    padded_frame = np.zeros((padded_row(self.x * 3), self.y), 'u1')
    padded_frame[:self.x * 3, :] = frame
    frame = padded_frame

    img = QtGui.QImage(frame, frame.shape[1], frame.shape[0], QtGui.QImage.Format_RGB888)
    pix = QtGui.QPixmap.fromImage(img)
    self.label.setPixmap(pix)
Szabolcs Dombi
  • 5,493
  • 3
  • 39
  • 71