Как эффективно поддерживать стабильный видеопоток с частотой кадров в приложении PyQt? ⇐ Python

Программы на Python
Anonymous
Как эффективно поддерживать стабильный видеопоток с частотой кадров в приложении PyQt?

Сообщение Anonymous »

Я разрабатываю приложение PyQt (PySide6), которому необходимо одновременно отображать и сохранять потоки с нескольких камер. Отображение потоков камеры проходит хорошо; однако хранение этих потоков, похоже, значительно замедляет работу приложения до такой степени, что графический интерфейс больше не работает плавно.
У меня есть минимальный рабочий пример с использованием заглушки, чтобы продемонстрировать, как мои код в настоящее время работает. Однако, учитывая, что это минимальный рабочий пример, заметно тормозить он не будет.

Код: Выделить всё

import sys
from time import sleep

import av
import numpy as np
import pyqtgraph as pg
from PySide6.QtCore import QThread, Signal, Slot, Qt
from PySide6.QtWidgets import QApplication, QHBoxLayout, QWidget, QVBoxLayout, QPushButton, QGroupBox

class RGBCameraStub(QThread):

newFrame = Signal(np.ndarray)

def __init__(self):
super().__init__()
self.killSwitch = True

def stop(self):
self.killSwitch = False
self.quit()
self.wait()

def run(self):
self.killSwitch = True
while self.killSwitch:
self.newFrame.emit((np.random.rand(1456, 1080, 3) * 255).astype(np.uint8))
sleep((20 + int(np.random.rand() * 30))/ 1000)

class VideoWriter(QThread):

def __init__(self):
super().__init__()
self.output_container = av.open('output_video.mkv', mode='w')
self.stream = self.output_container.add_stream('ffv1', rate=None)
self.stream.width = 1456
self.stream.height = 1080
self.stream.pix_fmt = 'yuv420p'

@Slot(np.ndarray)
def addFrame(self, frame: np.ndarray):
av_frame = av.VideoFrame.from_ndarray(frame, format='rgb24')
av_frame.pts = None # Leave emtpy for auto-handling - variable framerate?
for packet in self.stream.encode(av_frame):
self.output_container.mux(packet)

def stop(self):
self.output_container.close()
self.quit()
self.wait()

def run(self):
self.exec()

class VideoBox(QGroupBox):

def __init__(self, title):
super().__init__(title=title)
self.createLayout()
self.videoWidget.setImage((np.random.rand(1456, 1080, 3) * 255).astype(np.uint8))

def createLayout(self):
layout = QVBoxLayout()
self.videoWidget = pg.RawImageWidget()
layout.addWidget(self.videoWidget)
self.setLayout(layout)
self.setStyleSheet("""QGroupBox {
border: 1px solid #494B4F;
margin-top: 8px;
min-width: 180px;
min-height: 180px;
padding: 2px 0px 0px 0px;
}
QGroupBox::title {
color: #aeb0b8;
subcontrol-origin: margin;
subcontrol-position: top left;
left: 20px;
padding: 0 8px;
}""")

def setImage(self, data: np.ndarray):
self.videoWidget.setImage(data)

class MainWindow(QWidget):

closeSignal = Signal()

def __init__(self):
super().__init__()
self.setGeometry(0, 0, 900, 720)
self.createLayout()

def createLayout(self):
self.vimbaImage = VideoBox("RGB")
self.info = self.infoLayout()

layout = QVBoxLayout()
layout.addWidget(self.vimbaImage)
layout.addWidget(self.info)
self.setLayout(layout)

self.setAttribute(Qt.WA_StyledBackground, True)
self.setStyleSheet("MainWindow { background-color: #1e1f22;  }")

def infoLayout(self):
widget = QWidget()
layout = QVBoxLayout()

rgbButtonWidget = QWidget()
buttonLayout = QHBoxLayout()
self.connectButton = QPushButton('Connect', parent=self)
self.disconnectButton = QPushButton('Disconnect', parent=self)
buttonLayout.addWidget(self.connectButton)
buttonLayout.addWidget(self.disconnectButton)
buttonLayout.addStretch()
rgbButtonWidget.setLayout(buttonLayout)
layout.addWidget(rgbButtonWidget)

widget.setLayout(layout)
return widget

def closeEvent(self, event):
self.closeSignal.emit()
event.accept()

if __name__ == "__main__":
app = QApplication(sys.argv)

rgbCamera = RGBCameraStub()
videoWriter = VideoWriter()
videoWriter.start()

main_window = MainWindow()

# Button connections
main_window.connectButton.clicked.connect(rgbCamera.start)
main_window.disconnectButton.clicked.connect(rgbCamera.stop)
# main_window.disconnectButton.clicked.connect(videoWriter.stop)

# Display frames
rgbCamera.newFrame.connect(main_window.vimbaImage.setImage)

# Write frame to file
rgbCamera.newFrame.connect(videoWriter.addFrame)

# Close application
main_window.closeSignal.connect(rgbCamera.stop)
main_window.closeSignal.connect(videoWriter.stop)

main_window.show()
sys.exit(app.exec())

Поэтому мой вопрос(ы):

[*]Как я могу повысить производительность VideoWriter ? В настоящее время я добавляю кадр за кадром, как только поток камеры предоставляет новый кадр. Может быть, это не лучший подход?
[*]Частота кадров камеры не совсем стабильна, поэтому я установил av_frame.pts = None но, возможно, это тоже не тот подход брать?
[*]При таком коде полученный медиафайл быстро раздувается в размерах, есть ли способ справиться с этим без потери качества?
< /ul>
Кстати, в настоящее время я использую оболочку PyAV для библиотеки FFmpeg, однако открыт для других предложений.

Подробнее здесь: https://stackoverflow.com/questions/790 ... pplication

Вернуться в «Python»