学无先后,达者为师

网站首页 编程语言 正文

ffmpeg+pyqt5简单实现一个抽帧可视化小工具

作者:CrazyCosin 更新时间: 2022-05-13 编程语言

一、运行环境

        python3.6,linux18.04,pyqt5

二、主体代码

# -*- coding: utf-8 -*-

# Form implementation generated from reading ui file '/home/SENSETIME/wangwenjun1.vendor/PycharmProjects/pyqt/frame.ui'
#
# Created by: PyQt5 UI code generator 5.9.2
#
# WARNING! All changes made in this file will be lost!

from PyQt5 import QtCore, QtGui, QtWidgets
from PyQt5.QtWidgets import *
from PyQt5.QtCore import *
import os
import shutil
import time
from subprocess import PIPE, Popen
from uuid import uuid4
from multiprocessing import Queue


class Ui_MainWindow(QtWidgets.QMainWindow):

    def __init__(self):
        super(Ui_MainWindow, self).__init__()
        self.setupUi(self)
        self.retranslateUi(self)

    def setupUi(self, MainWindow):
        MainWindow.setObjectName("MainWindow")
        MainWindow.resize(800, 600)
        self.centralwidget = QtWidgets.QWidget(MainWindow)
        self.centralwidget.setObjectName("centralwidget")
        self.pushButton = QtWidgets.QPushButton(self.centralwidget)
        self.pushButton.setGeometry(QtCore.QRect(90, 20, 80, 23))
        self.pushButton.setObjectName("pushButton")
        self.progressBar = QtWidgets.QProgressBar(self.centralwidget)
        self.progressBar.setGeometry(QtCore.QRect(530, 280, 221, 23))
        self.progressBar.setProperty("value", 0)
        self.progressBar.setObjectName("progressBar")
        self.pushButton_2 = QtWidgets.QPushButton(self.centralwidget)
        self.pushButton_2.setGeometry(QtCore.QRect(530, 240, 80, 23))
        self.pushButton_2.setObjectName("pushButton_2")
        self.listView_2 = QtWidgets.QListView(self.centralwidget)
        self.listView_2.setGeometry(QtCore.QRect(80, 80, 441, 201))
        self.listView_2.setObjectName("listView_2")
        self.listView = QtWidgets.QListView(self.centralwidget)
        self.listView.setGeometry(QtCore.QRect(80, 310, 441, 211))
        self.listView.setObjectName("listView")
        self.label = QtWidgets.QLabel(self.centralwidget)
        self.label.setGeometry(QtCore.QRect(80, 60, 121, 16))
        self.label.setObjectName("label")
        self.label_2 = QtWidgets.QLabel(self.centralwidget)
        self.label_2.setGeometry(QtCore.QRect(80, 290, 211, 16))
        self.label_2.setObjectName("label_2")
        MainWindow.setCentralWidget(self.centralwidget)
        self.menubar = QtWidgets.QMenuBar(MainWindow)
        self.menubar.setGeometry(QtCore.QRect(0, 0, 800, 20))
        self.menubar.setObjectName("menubar")
        MainWindow.setMenuBar(self.menubar)
        self.statusbar = QtWidgets.QStatusBar(MainWindow)
        self.statusbar.setObjectName("statusbar")
        MainWindow.setStatusBar(self.statusbar)

        self.retranslateUi(MainWindow)
        QtCore.QMetaObject.connectSlotsByName(MainWindow)

        self.pushButton_2.clicked.connect(self.onStart)
        self.timer = QBasicTimer()
        self.step = 0
        self.failed_file = Queue()
        self.mp4_files = []
        self.target_folder = 'picture'
        self.finish_file = Queue()

        self.pushButton.clicked.connect(self.openfile)

        # 实例化列表模型,添加数据
        self.slm = QStringListModel()
        self.failed_slm = QStringListModel()

    def retranslateUi(self, MainWindow):
        _translate = QtCore.QCoreApplication.translate
        MainWindow.setWindowTitle(_translate("MainWindow", "视频抽帧小工具"))
        self.pushButton.setText(_translate("MainWindow", "打开"))
        self.pushButton_2.setText(_translate("MainWindow", "开始"))
        self.label.setText(_translate("MainWindow", "总量"))
        self.label_2.setText(_translate("MainWindow", "失败: 0"))

    def openfile(self):
        openfile_name = QFileDialog.getOpenFileNames(self, '选择文件', '', 'Excel files(*.mp4)')
        self.mp4_files = list(self._filter_file(openfile_name[0]))
        self.target_folder = os.path.join(os.path.split(self.mp4_files[0])[0], self.target_folder)
        QMessageBox.warning(self, '保存路径', f'本次图片保存在{self.target_folder}, 默认会清理该路径,请及时转移文件!')
        try:
            shutil.rmtree(self.target_folder)
        except:
            pass
        print(f'total: {len(self.mp4_files)}')
        self.label.setText(f"总量: {len(self.mp4_files)}")
        # 设置模型列表视图,加载数据列表
        self.slm.setStringList(self.mp4_files)

        # 设置列表视图的模型
        self.listView_2.setModel(self.slm)

    def _filter_file(self, mp4_files):
        t_set = set()
        for filename in mp4_files:
            if os.path.isfile(filename):
                new_f = filename.replace(' ', '').replace('(', '').replace(')', '').strip()
                try:
                    os.rename(filename, new_f)
                except shutil.SameFileError:
                    print('no copy!')
                except:
                    import traceback
                    print(traceback.format_exc())
                if new_f in t_set:
                    continue
                t_set.add(new_f)
        return t_set

    def timerEvent(self, event):
        self.progressBar.setValue(self.finish_file.qsize() * 100 / len(self.mp4_files))
        self.label_2.setText(f"失败: {self.failed_file.qsize()}")
        # 设置模型列表视图,加载数据列表
        _t = []
        while not self.failed_file.empty():
            _t.append(self.failed_file.get())
        self.failed_slm.setStringList(_t)
        # 设置列表视图的模型
        self.listView.setModel(self.failed_slm)
        if self.finish_file.qsize() >= len(self.mp4_files):
            print(self.finish_file.qsize())
            self.timer.stop()
            self.pushButton_2.setText('Start')
            QMessageBox.information(self, '抽帧完成提醒', f'本次图片保存在{self.target_folder}')
            self.finish_file = Queue()
            self.failed_file = Queue()
            return

    def onStart(self):
        self.label.setText(f"总量: {len(self.mp4_files)}")
        if not len(self.mp4_files):
            QMessageBox.critical(self, '抽帧视频数错误', '总量数为0不可执行抽帧操作!')
            return
        if self.timer.isActive():
            self.proc.terminate()
            self.proc.join()
            self.timer.stop()
            self.mp4_files.clear()
            self.progressBar.setValue(0)
            self.label.setText(f"总量: {len(self.mp4_files)}")
            self.listView_2.setModel(self.slm.setStringList([]))
            self.listView.setModel(self.slm.setStringList([]))
            self.pushButton_2.setText('Start')
            self.finish_file = Queue()
            self.failed_file = Queue()
        else:
            self.timer.start(0, self)
            from multiprocessing import Process
            self.proc = Process(target=self.extract_frame_files, args=(self.mp4_files, self.finish_file, self.failed_file))
            self.proc.start()
            self.pushButton_2.setText('End')

    def extract_frame_files(self, files, finish_q, failed_q):
        for file in files:
            self.extract_frame(file, finish_q, failed_q)

    def extract_frame(self, file,  finish_q, failed_q):
        picture_id = str(uuid4())
        os.makedirs(self.target_folder, exist_ok=True)
        command = f"ffmpeg -i {file} -vf fps=2 {self.target_folder}/frame{picture_id}_xxx.png"
        command = command.replace('xxx', '%d')
        proc = Popen(command, shell=True,
                     stdout=PIPE,
                     stderr=PIPE)
        return_code = None
        while return_code is None:
            return_code = proc.poll()
            time.sleep(0.01)
        out, err = proc.communicate()
        proc.kill()
        finish_q.put(1)
        if return_code == 0:
            pass
        else:
            failed_q.put(1)
            print(f'[{file}]出错结束')
            print(f"err: \n{err}")


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

三、运行效果

        

 

原文链接:https://blog.csdn.net/CrazyCosin/article/details/119885851

栏目分类
最近更新