首页 > 解决方案 > 话题成员“分享”

问题描述

背景和问题:

我正在尝试创建一个自定义程序来编辑视频上的“事物”。为此,我希望能够使用时间滑块在视频中导航。从 Internet 上的大量资源中获得灵感,我编写了下面的代码,它非常适合简单阅读。但是当我使用滑块更改视频当前时间时,有时它会崩溃。

研究与测试:

我知道它来自线程中的视频读取处理程序,主读取进程(run()函数)和我的自定义函数同时使用它来更改视频时间位置。我尝试了不同的方法,例如添加自动将视频设置为“暂停”状态、添加“is_reading 位”或“正在阅读时等待”,但它永远不会结束,或者仍然崩溃

这是一个工作(并且崩溃!)的简化代码示例

有没有比我有更多线程经验的人可以引导我使用线程来解决我的并发/不共享成员问题?

#!/usr/bin/python
# coding: utf-8

import cv2
import sys
from PyQt5.QtWidgets import  QWidget, QLabel, QApplication, QPushButton, QSlider, QHBoxLayout, QVBoxLayout
from PyQt5.QtCore import QThread, Qt, pyqtSignal, pyqtSlot
from PyQt5.QtGui import QImage, QPixmap
import time

class Thread(QThread):
    changePixmap = pyqtSignal(QImage)
    # reading lock bit
    is_reading_handler = False
    # Play / pause status bit
    is_pause = False
    # vidéo frame rate
    fps = 50.0
    # current frame video index
    current_frame_index = 0
    # screen time by image
    image_duration_ns = 1000000000.0/50.0

    # method to change the play/pause status
    def swapPlayPause(self):
        if self.is_pause:
            self.is_pause = False
            print("Play")
        else:
            self.is_pause = True
            print("Pause")

    # method to change de current time video position
    def changeVideoPosition(self, value):
        # self.is_pause = True # <- test
        self.current_frame_index = int(self.frames_count*value*0.001)
        self.video_handler.set(cv2.CAP_PROP_POS_FRAMES, self.current_frame_index)
        # self.read()

    def run(self):
        # Open video handler
        self.video_handler = cv2.VideoCapture("video_source/GOPR0483.MP4")

        # GET VIDEO INFORMATIONS

        # Find OpenCV version
        (major_ver, minor_ver, subminor_ver) = (cv2.__version__).split('.')

        if int(major_ver) < 3 :
            fps = self.video_handler.get(cv2.cv.CV_CAP_PROP_FPS)
            print("Frames per second using video.get(cv2.cv.CV_CAP_PROP_FPS): {0}".format(fps))
        else :
            fps = self.video_handler.get(cv2.CAP_PROP_FPS)
            print("Frames per second using video.get(cv2.CAP_PROP_FPS) : {0}".format(fps))

        width  = self.video_handler.get(cv2.CAP_PROP_FRAME_WIDTH)
        height = self.video_handler.get(cv2.CAP_PROP_FRAME_HEIGHT)
        self.width = 640
        self.height = 480
        self.frames_count = self.video_handler.get(cv2.CAP_PROP_FRAME_COUNT)
        print("frames count = {0}".format(self.frames_count))
        duration = self.frames_count / fps
        print("duration in s {0}".format(duration))

        # "Read" video

        time_last = 0
        # image screen time
        self.image_duration_ns = 1000000000/fps

        while True:
            if not self.is_pause:
                # new image if the current one is expired
                time_cur = time.time_ns()
                if time_cur-time_last > self.image_duration_ns:
                    # read the new image
                    self.read()
                    time_last = time_cur

    def read(self):

        # if the reading handler is already used, wait a litle bit before to read
        if self.is_reading_handler:
            time.sleep(0.01)

            # This "while" does not work because the self.is_reading_heandler does not seems to be share with the run() member !?
            # while self.is_reading_handler:
                # time.sleep(0.01)
            
        # lock the reading handler
        self.is_reading_handler = True

        # read the image
        ret, frame = self.video_handler.read()
        if ret:
            # https://stackoverflow.com/a/55468544/6622587
            rgbImage = cv2.cvtColor(frame, cv2.COLOR_BGR2RGB)
            h, w, ch = rgbImage.shape
            bytesPerLine = ch * w
            convertToQtFormat = QImage(rgbImage.data, w, h, bytesPerLine, QImage.Format_RGB888)
            p = convertToQtFormat.scaled(self.width, self.height, Qt.KeepAspectRatio)
            self.changePixmap.emit(p)
            self.current_frame_index += 1

        # release read handler
        self.is_reading_handler = False

              
class App(QWidget):

    def __init__(self):
        super().__init__()
        self.title = "test"
        self.initUI()

    @pyqtSlot(QImage)
    def setImage(self, image):
        self.video.setPixmap(QPixmap.fromImage(image))

    def initUI(self):
        self.setWindowTitle(self.title)
        
        # QT object for the video
        self.video = QLabel(self)
        self.video.resize(self.width(), self.height())

        # video read thread
        self.th = Thread(self)
        self.th.changePixmap.connect(self.setImage)
        
        # BT play pause
        self.bt_pp = QPushButton('P/P', self)
        self.bt_pp.clicked.connect(self.th.swapPlayPause)

        # video time slider
        self.sl_time = QSlider(Qt.Horizontal, self)
        self.sl_time.setGeometry(10, 100, 400, 40)
        self.sl_time.setMinimum(0)
        self.sl_time.setMaximum(1000)
        self.time_slide_value = 0
        self.sl_time.setValue(self.time_slide_value)
        self.sl_time.valueChanged.connect(self.th.changeVideoPosition)

        # simple vert layout
        self.ly_all = QVBoxLayout()
        self.ly_all.addWidget(self.video)
        self.ly_all.addWidget(self.sl_time)
        self.ly_all.addWidget(self.bt_pp)
        self.setLayout(self.ly_all)

        # here we go !
        self.th.start()
        self.show()

# instanciation
app = QApplication(sys.argv)
window = App()
app.exec_()

标签: pythonmultithreadingpyqt5

解决方案


推荐阅读