从相机 Pyqt5 调整直播帧大小

Posted

技术标签:

【中文标题】从相机 Pyqt5 调整直播帧大小【英文标题】:Resize livestream frame size from camera Pyqt5 【发布时间】:2021-11-22 05:48:18 【问题描述】:

我使用 pyqt5 创建了三个窗口。它们如下所示。

1.主窗口

单击开始按钮时,会打开下一个窗口,如下所示。

2。中间窗口

此窗口接受流媒体链接以连接相机。对于普通的 USB 相机,它是“0”。当输入 0 时,下一个窗口打开。

3.输出窗口

我在这里面临的问题是我无法调整输出帧的大小,尽管我可以调整屏幕大小。

**下面附上最后一个窗口的代码,**

import sys
from PyQt5.QtGui import *
from PyQt5.QtWidgets import *
from PyQt5.QtCore import *
import cv2
class Ui_OutputDialog(QWidget):
    def __init__(self):
        super(Ui_OutputDialog, self).__init__()

        self.VBL = QVBoxLayout()

        self.FeedLabel = QLabel()
        self.VBL.addWidget(self.FeedLabel)

        self.CancelBTN = QPushButton("Cancel")
        self.CancelBTN.clicked.connect(self.CancelFeed)
        self.VBL.addWidget(self.CancelBTN)

        self.Worker1 = Worker1()

        self.Worker1.start()
        self.Worker1.ImageUpdate.connect(self.ImageUpdateSlot)
        self.setLayout(self.VBL)

    def ImageUpdateSlot(self, Image):
        self.FeedLabel.setPixmap(QPixmap.fromImage(Image))

    def CancelFeed(self):
        self.Worker1.stop()

class Worker1(QThread):
    ImageUpdate = pyqtSignal(QImage)
    def run(self):
        self.ThreadActive = True
        Capture = cv2.VideoCapture(0)
        while self.ThreadActive:
            ret, frame = Capture.read()
            if ret:
                Image = cv2.cvtColor(frame, cv2.COLOR_BGR2RGB)
                FlippedImage = cv2.flip(Image, 1)
                ConvertToQtFormat = QImage(FlippedImage.data, FlippedImage.shape[1], FlippedImage.shape[0], QImage.Format_RGB888)
                Pic = ConvertToQtFormat.scaled(720, 480, Qt.KeepAspectRatio)
                self.ImageUpdate.emit(Pic)
    def stop(self):
        self.ThreadActive = False
        self.quit()

if __name__ == "__main__":
    App = QApplication(sys.argv)
    Root = Ui_OutputDialog()
    Root.show()
    sys.exit(App.exec())

附上第一个文件的代码

import sys
from PyQt5.uic import loadUi
from PyQt5 import QtWidgets
from PyQt5.QtCore import pyqtSlot
from PyQt5.QtWidgets import QApplication, QDialog
import resource
from middlewindow import Ui_MiddleDialog 

class Ui_Dialog(QDialog):
    def __init__(self):
        super(Ui_Dialog, self).__init__()
        loadUi("mainwindow.ui", self)

        self.main_click.clicked.connect(self.runmiddlewindow)


    def runmiddlewindow(self):
        """
        Called when the user presses the Run button
        """
        print("Clicked Run")
        ui.hide()  # hide the main window
        self.middleWindow_()  # Create and open new output window

    def middleWindow_(self):
        """
        Created new window for vidual output of the video in GUI
        """
        self._new_window = Ui_MiddleDialog()
        self._new_window.show()
        #self._new_window.startVideo(self.Videocapture_)
        #print("Video Played")


if __name__ == "__main__":
    app = QApplication(sys.argv)
    ui = Ui_Dialog()
    ui.show()
    sys.exit(app.exec_())

附上第一个文件的用户界面

<?xml version="1.0" encoding="UTF-8"?>
<ui version="4.0">
 <class>Dialog</class>
 <widget class="QDialog" name="Dialog">
  <property name="geometry">
   <rect>
    <x>0</x>
    <y>0</y>
    <width>508</width>
    <height>259</height>
   </rect>
  </property>
  <property name="minimumSize">
   <size>
    <width>508</width>
    <height>259</height>
   </size>
  </property>
  <property name="maximumSize">
   <size>
    <width>508</width>
    <height>259</height>
   </size>
  </property>
  <property name="windowTitle">
   <string>Face Recognition Time Attendance App</string>
  </property>
  <property name="windowIcon">
   <iconset>
    <normaloff>:iconer.png</normaloff>:iconer.png</iconset>
  </property>
  <property name="styleSheet">
   <string notr="true">background-color: rgb(255, 255, 255);
border-color: rgb(0, 0, 0);</string>
  </property>
  <widget class="QWidget" name="verticalLayoutWidget_2">
   <property name="geometry">
    <rect>
     <x>40</x>
     <y>20</y>
     <width>436</width>
     <height>224</height>
    </rect>
   </property>
   <layout class="QGridLayout" name="gridLayout">
    <item row="2" column="0">
     <widget class="QPushButton" name="main_click">
      <property name="font">
       <font>
        <family>Roboto Condensed Light</family>
        <pointsize>13</pointsize>
       </font>
      </property>
      <property name="styleSheet">
       <string notr="true">background-color: rgb(170, 0, 0);
background-color: qlineargradient(spread:pad, x1:0, y1:0, x2:1, y2:0, stop:0 rgba(0, 0, 0, 255), stop:1 rgba(255, 255, 255, 255));
border-color: rgb(8, 8, 8);
background-color: rgb(147, 147, 147);</string>
      </property>
      <property name="text">
       <string>START</string>
      </property>
     </widget>
    </item>
    <item row="1" column="0">
     <layout class="QHBoxLayout" name="horizontalLayout_3">
      <item>
       <widget class="QLabel" name="logolabel_2">
        <property name="styleSheet">
         <string notr="true">border-color: rgb(11, 11, 11);</string>
        </property>
        <property name="text">
         <string/>
        </property>
        <property name="pixmap">
         <pixmap>loger.png</pixmap>
        </property>
       </widget>
      </item>
      <item>
       <widget class="QLabel" name="filelabel_3">
        <property name="enabled">
         <bool>true</bool>
        </property>
        <property name="font">
         <font>
          <family>Roboto Condensed</family>
          <pointsize>14</pointsize>
          <weight>75</weight>
          <bold>true</bold>
         </font>
        </property>
        <property name="styleSheet">
         <string notr="true">border-color: rgb(12, 12, 12);
border-color: rgb(0, 0, 0);</string>
        </property>
        <property name="text">
         <string>&lt;html&gt;&lt;head/&gt;&lt;body&gt;&lt;p&gt;&lt;span style=&quot; color:#1113aa;&quot;&gt;BARCODE SCANNER&lt;/span&gt;&lt;/p&gt;&lt;/body&gt;&lt;/html&gt;</string>
        </property>
       </widget>
      </item>
     </layout>
    </item>
   </layout>
  </widget>
 </widget>
 <resources>
  <include location="resource.qrc"/>
  <include location="resource.qrc"/>
 </resources>
 <connections/>
</ui>

附上第二个文件的代码

from PyQt5.QtGui import QImage, QPixmap
from PyQt5.uic import loadUi
from PyQt5.QtCore import pyqtSlot, QTimer
from PyQt5.QtWidgets import QDialog
import cv2
import numpy as np
import datetime
import os
from lastwindow import Ui_OutputDialog

class Ui_MiddleDialog(QDialog):
    def __init__(self):
        
        super(Ui_MiddleDialog, self).__init__()
        loadUi("./middlewindow.ui", self)
        

        self.press.clicked.connect(self.connect_camera)
        self._new_window = None


    def runmiddlewindow(self):
    
        print("Clicked Run")
        #print(self.Videocapture_)
    
    def connect_camera(self):
        
        stream = self.stream.text()
        self.Videocapture_ = stream
        #ui.hide()  # hide the main window
        self.outputWindow_()  # Create and open new output window

    def outputWindow_(self):
        
        self._new_window = Ui_OutputDialog()
        self._new_window.show()
        #self._new_window.controlTimer(self.Videocapture_)
        

附上第二个文件的 UI 文件

<?xml version="1.0" encoding="UTF-8"?>
<ui version="4.0">
 <class>Dialog</class>
 <widget class="QDialog" name="Dialog">
  <property name="geometry">
   <rect>
    <x>0</x>
    <y>0</y>
    <width>508</width>
    <height>259</height>
   </rect>
  </property>
  <property name="minimumSize">
   <size>
    <width>508</width>
    <height>259</height>
   </size>
  </property>
  <property name="maximumSize">
   <size>
    <width>508</width>
    <height>259</height>
   </size>
  </property>
  <property name="windowTitle">
   <string>Face Recognition Time Attendance App</string>
  </property>
  <property name="windowIcon">
   <iconset>
    <normaloff>iconer.png</normaloff>iconer.png</iconset>
  </property>
  <property name="styleSheet">
   <string notr="true">border-color: rgb(7, 7, 7);</string>
  </property>
  <widget class="QLineEdit" name="stream">
   <property name="geometry">
    <rect>
     <x>10</x>
     <y>120</y>
     <width>481</width>
     <height>24</height>
    </rect>
   </property>
  </widget>
  <widget class="QPushButton" name="press">
   <property name="geometry">
    <rect>
     <x>0</x>
     <y>190</y>
     <width>511</width>
     <height>36</height>
    </rect>
   </property>
   <property name="font">
    <font>
     <family>Roboto Condensed Light</family>
     <pointsize>13</pointsize>
    </font>
   </property>
   <property name="styleSheet">
    <string notr="true">background-color: rgb(170, 0, 0);
background-color: qlineargradient(spread:pad, x1:0, y1:0, x2:1, y2:0, stop:0 rgba(0, 0, 0, 255), stop:1 rgba(255, 255, 255, 255));
border-color: rgb(8, 8, 8);
background-color: rgb(147, 147, 147);</string>
   </property>
   <property name="text">
    <string>CONFIRM</string>
   </property>
  </widget>
  <widget class="QLabel" name="label">
   <property name="geometry">
    <rect>
     <x>10</x>
     <y>40</y>
     <width>491</width>
     <height>51</height>
    </rect>
   </property>
   <property name="text">
    <string>&lt;html&gt;&lt;head/&gt;&lt;body&gt;&lt;p align=&quot;center&quot;&gt;&lt;span style=&quot; font-size:12pt; font-weight:600; color:#1113aa;&quot;&gt;ENTER THE STREAMLINK TO CONNECT&lt;/span&gt;&lt;/p&gt;&lt;/body&gt;&lt;/html&gt;</string>
   </property>
  </widget>
 </widget>
 <resources>
  <include location="../Face_Detection_PyQt_Final/resource.qrc"/>
 </resources>
 <connections/>
</ui>

【问题讨论】:

【参考方案1】:

重新实现resizeEvent 以设置图像应缩放到的大小。您需要将布局的大小约束设置为SetNoConstraint,以使窗口变小或变大。您还应该在停止后释放捕获。

class Ui_OutputDialog(QWidget):
    def __init__(self):
        super(Ui_OutputDialog, self).__init__()

        self.VBL = QVBoxLayout()

        self.FeedLabel = QLabel()
        self.VBL.addWidget(self.FeedLabel)

        self.CancelBTN = QPushButton("Cancel")
        self.CancelBTN.clicked.connect(self.CancelFeed)
        self.VBL.addWidget(self.CancelBTN)

        self.Worker1 = Worker1()
        
        self.Worker1.start()
        self.Worker1.ImageUpdate.connect(self.ImageUpdateSlot)
        self.setLayout(self.VBL)

        self.VBL.setSizeConstraint(QLayout.SetNoConstraint)
        self.resize(720, 480)

    def resizeEvent(self, event):
        super().resizeEvent(event)
        self.Worker1.size = self.FeedLabel.size()

    def ImageUpdateSlot(self, Image):
        self.FeedLabel.setPixmap(QPixmap.fromImage(Image))

    def CancelFeed(self):
        self.Worker1.stop()
        

class Worker1(QThread):
    ImageUpdate = pyqtSignal(QImage)
    def run(self):
        self.ThreadActive = True
        Capture = cv2.VideoCapture(0)
        while self.ThreadActive:
            ret, frame = Capture.read()
            if ret:
                Image = cv2.cvtColor(frame, cv2.COLOR_BGR2RGB)
                FlippedImage = cv2.flip(Image, 1)
                ConvertToQtFormat = QImage(FlippedImage.data, FlippedImage.shape[1], FlippedImage.shape[0], QImage.Format_RGB888)
                Pic = ConvertToQtFormat.scaled(self.size, Qt.KeepAspectRatio)
                self.ImageUpdate.emit(Pic)
                
        Capture.release()
        
    def stop(self):
        self.ThreadActive = False
        self.quit()

【讨论】:

非常感谢@alec。我通过删除 Qt.KeepAspectRatio 修复了同样的问题。 哦,我以为你想保持纵横比。否则你可以使用QLabel.setScaledContents(True)

以上是关于从相机 Pyqt5 调整直播帧大小的主要内容,如果未能解决你的问题,请参考以下文章

视频直播首帧速度优化

如何将视频帧推送到 obs 服务器以在 Android 上进行移动直播

使用hdmi高清相机进行低延时直播的方法

视频直播技术:延迟优化

直播技术细节1

直播开发步骤