使用pyside6实现ffmpeg *** 作的图形化

使用pyside6实现ffmpeg *** 作的图形化,第1张

ffmpeg

ffmpeg是一个音视频处理工具。


常用的功能有视频转图片和截取视频片段。


视频转图片
$ ffmpeg -i [input] -vf fps=[fps] -q:v [n]  [output]"
截取视频
$ ffmpeg -ss [start] -i [input] -t [duration] -c copy [output]
$ ffmpeg -ss [start] -i [input] -to [end] -c copy [output]

其中,-i 表示输入;-vf 表示每1秒输出几帧;-q:v 选择图片质量,其中n是1-31的数字,1表示最高质量

附一张有无添加-q:v 1参数的对比图:

pyside6

当批量 *** 作视频,且需要频繁更改传入参数时,使用可视化界面可以提升效率。



pyside6是Qt6的python版本下的API库,其内置的designer可以使用拖拽的方式生成页面代码。



下面记录实现的过程,下图是视频抽帧、裁剪的界面。



conda create -n pyside6 python=3.8
conda activate pyside6
pip install pyside6 -y

打开D:\ProgramFiles\Anaconda\envs\pyside6\Lib\site-packages\PySide6\designer.exe,进行拖拽式界面设计,如下图:

这里使用 Label、LineEdit、comboBox、pushButton四个组件,进行简单布局后,保存为 ui.ui文件。



打开命令行工具,编译ui.ui得到python文件ui.py

PySide6-uic ui.ui -o ui.py

关于快速熟悉Pyside6设计 *** 作的可以参考视频:

https://www.bilibili.com/video/BV1Wa41127fk/?spm_id_from=333.788

生成的界面代码:ui.py
# -*- coding: utf-8 -*-

################################################################################
## Form generated from reading UI file 'ui.ui'
##
## Created by: Qt User Interface Compiler version 6.2.4
##
## WARNING! All changes made in this file will be lost when recompiling UI file!
################################################################################

from PySide6.QtCore import (QCoreApplication, QDate, QDateTime, QLocale,
    QMetaObject, QObject, QPoint, QRect,
    QSize, QTime, QUrl, Qt)
from PySide6.QtGui import (QBrush, QColor, QConicalGradient, QCursor,
    QFont, QFontDatabase, QGradient, QIcon,
    QImage, QKeySequence, QLinearGradient, QPainter,
    QPalette, QPixmap, QRadialGradient, QTransform)
from PySide6.QtWidgets import (QApplication, QComboBox, QHBoxLayout, QLabel,
    QLineEdit, QMainWindow, QMenuBar, QPushButton,
    QSizePolicy, QSpacerItem, QStatusBar, QVBoxLayout,
    QWidget)

from QLineEdit_Override import MyQLine

class Ui_MainWindow(object):
    def setupUi(self, MainWindow):
        if not MainWindow.objectName():
            MainWindow.setObjectName(u"MainWindow")
        MainWindow.resize(1116, 913)
        self.centralwidget = QWidget(MainWindow)
        self.centralwidget.setObjectName(u"centralwidget")
        self.comboBox_fps = QComboBox(self.centralwidget)
        self.comboBox_fps.addItem("")
        self.comboBox_fps.addItem("")
        self.comboBox_fps.addItem("")
        self.comboBox_fps.addItem("")
        self.comboBox_fps.addItem("")
        self.comboBox_fps.addItem("")
        self.comboBox_fps.setObjectName(u"comboBox_fps")
        self.comboBox_fps.setGeometry(QRect(270, 250, 91, 31))
        self.comboBox_output = QComboBox(self.centralwidget)
        self.comboBox_output.addItem("")
        self.comboBox_output.addItem("")
        self.comboBox_output.setObjectName(u"comboBox_output")
        self.comboBox_output.setGeometry(QRect(270, 360, 171, 31))
        self.layoutWidget = QWidget(self.centralwidget)
        self.layoutWidget.setObjectName(u"layoutWidget")
        self.layoutWidget.setGeometry(QRect(190, 120, 71, 301))
        self.verticalLayout = QVBoxLayout(self.layoutWidget)
        self.verticalLayout.setObjectName(u"verticalLayout")
        self.verticalLayout.setContentsMargins(0, 0, 0, 0)
        self.label = QLabel(self.layoutWidget)
        self.label.setObjectName(u"label")

        self.verticalLayout.addWidget(self.label)

        self.label_2 = QLabel(self.layoutWidget)
        self.label_2.setObjectName(u"label_2")

        self.verticalLayout.addWidget(self.label_2)

        self.label_3 = QLabel(self.layoutWidget)
        self.label_3.setObjectName(u"label_3")

        self.verticalLayout.addWidget(self.label_3)

        self.lineEdit_file_path = MyQLine(self.centralwidget)
        self.lineEdit_file_path.setObjectName(u"lineEdit_file_path")
        self.lineEdit_file_path.setGeometry(QRect(270, 130, 301, 61))
        self.widget = QWidget(self.centralwidget)
        self.widget.setObjectName(u"widget")
        self.widget.setGeometry(QRect(630, 210, 61, 221))
        self.verticalLayout_2 = QVBoxLayout(self.widget)
        self.verticalLayout_2.setObjectName(u"verticalLayout_2")
        self.verticalLayout_2.setContentsMargins(0, 0, 0, 0)
        self.label_4 = QLabel(self.widget)
        self.label_4.setObjectName(u"label_4")

        self.verticalLayout_2.addWidget(self.label_4)

        self.label_5 = QLabel(self.widget)
        self.label_5.setObjectName(u"label_5")

        self.verticalLayout_2.addWidget(self.label_5)

        self.widget1 = QWidget(self.centralwidget)
        self.widget1.setObjectName(u"widget1")
        self.widget1.setGeometry(QRect(720, 170, 171, 301))
        self.verticalLayout_3 = QVBoxLayout(self.widget1)
        self.verticalLayout_3.setObjectName(u"verticalLayout_3")
        self.verticalLayout_3.setContentsMargins(0, 0, 0, 0)
        self.lineEdit_start = QLineEdit(self.widget1)
        self.lineEdit_start.setObjectName(u"lineEdit_start")

        self.verticalLayout_3.addWidget(self.lineEdit_start)

        self.lineEdit_duration = QLineEdit(self.widget1)
        self.lineEdit_duration.setObjectName(u"lineEdit_duration")

        self.verticalLayout_3.addWidget(self.lineEdit_duration)

        self.widget2 = QWidget(self.centralwidget)
        self.widget2.setObjectName(u"widget2")
        self.widget2.setGeometry(QRect(270, 510, 521, 61))
        self.horizontalLayout = QHBoxLayout(self.widget2)
        self.horizontalLayout.setObjectName(u"horizontalLayout")
        self.horizontalLayout.setContentsMargins(0, 0, 0, 0)
        self.pushButton_extract_frame = QPushButton(self.widget2)
        self.pushButton_extract_frame.setObjectName(u"pushButton_extract_frame")
        self.pushButton_extract_frame.setLayoutDirection(Qt.RightToLeft)

        self.horizontalLayout.addWidget(self.pushButton_extract_frame)

        self.horizontalSpacer = QSpacerItem(40, 20, QSizePolicy.Expanding, QSizePolicy.Minimum)

        self.horizontalLayout.addItem(self.horizontalSpacer)

        self.pushButton_video_clip = QPushButton(self.widget2)
        self.pushButton_video_clip.setObjectName(u"pushButton_video_clip")
        self.pushButton_video_clip.setLayoutDirection(Qt.RightToLeft)

        self.horizontalLayout.addWidget(self.pushButton_video_clip)

        MainWindow.setCentralWidget(self.centralwidget)
        self.menubar = QMenuBar(MainWindow)
        self.menubar.setObjectName(u"menubar")
        self.menubar.setGeometry(QRect(0, 0, 1116, 22))
        MainWindow.setMenuBar(self.menubar)
        self.statusbar = QStatusBar(MainWindow)
        self.statusbar.setObjectName(u"statusbar")
        MainWindow.setStatusBar(self.statusbar)

        self.retranslateUi(MainWindow)

        QMetaObject.connectSlotsByName(MainWindow)
    # setupUi

    def retranslateUi(self, MainWindow):
        MainWindow.setWindowTitle(QCoreApplication.translate("MainWindow", u"MainWindow", None))
        self.comboBox_fps.setItemText(0, QCoreApplication.translate("MainWindow", u"2", None))
        self.comboBox_fps.setItemText(1, QCoreApplication.translate("MainWindow", u"1.5", None))
        self.comboBox_fps.setItemText(2, QCoreApplication.translate("MainWindow", u"1", None))
        self.comboBox_fps.setItemText(3, QCoreApplication.translate("MainWindow", u"1/2", None))
        self.comboBox_fps.setItemText(4, QCoreApplication.translate("MainWindow", u"1/3", None))
        self.comboBox_fps.setItemText(5, QCoreApplication.translate("MainWindow", u"1/5", None))

        self.comboBox_output.setItemText(0, QCoreApplication.translate("MainWindow", u"Images", None))
        self.comboBox_output.setItemText(1, QCoreApplication.translate("MainWindow", u"\u540c\u540d\u6587\u4ef6\u5939", None))

        self.label.setText(QCoreApplication.translate("MainWindow", u"\u89c6\u9891\u6587\u4ef6", None))
        self.label_2.setText(QCoreApplication.translate("MainWindow", u"fps", None))
        self.label_3.setText(QCoreApplication.translate("MainWindow", u"\u4fdd\u5b58\u8def\u5f84", None))
        self.lineEdit_file_path.setText("")
        self.lineEdit_file_path.setPlaceholderText(QCoreApplication.translate("MainWindow", u"\u6d4f\u89c8\u6216\u62d6\u62fd.mp4\u6587\u4ef6\u5230\u8fd9\u91cc", None))
        self.label_4.setText(QCoreApplication.translate("MainWindow", u"\u5f00\u59cb\u65f6\u95f4", None))
        self.label_5.setText(QCoreApplication.translate("MainWindow", u"\u6301\u7eed\u65f6\u95f4", None))
        self.lineEdit_start.setText(QCoreApplication.translate("MainWindow", u"0", None))
        self.lineEdit_duration.setText(QCoreApplication.translate("MainWindow", u"0", None))
        self.pushButton_extract_frame.setText(QCoreApplication.translate("MainWindow", u"\u62bd\u5e27", None))
        self.pushButton_video_clip.setText(QCoreApplication.translate("MainWindow", u"\u88c1\u526a", None))
    # retranslateUi


接着,编写代码,获取组件的数值或者给组件绑定动作。


主函数:main.py
from PySide6.QtWidgets import QApplication, QMainWindow
from ui import Ui_MainWindow
from ffmpeg import extract_videos_frame, extract_video_frame, video_clip
import os
# PySide6-uic ui.ui -o ui.py


class MainWindow(QMainWindow):
    def __init__(self):
        super(MainWindow, self).__init__()
        self.ui = Ui_MainWindow()  # UI类的实例化
        self.ui.setupUi(self)

        self.band()
        self.fps = 0  # 帧率
        self.input_path = ""        # 视频路径
        self.output_path = ""  # 保存路径

        self.start = 0          # 开始时间
        self.duration = 0       # 持续时间

    def band(self):
        # seLf.ui.___ACTTON___.triggered.connect(___FUNCTION___)
        # seLf.ui.___BUTTON___.clicked.connect(___FUNCTION___)
        # seLf.Ui.___COMBO_B0X___.currentIndexChanged.connect(___FUNCTION___)
        # seLf.ui.___SPIN_B0X___.valueChanged.connect(___FUNCTION___)
        # 自定义信号.属性名.connect(___FUNCTION___)
        self.ui.pushButton_extract_frame.clicked.connect(self.extract_frame)
        self.ui.pushButton_video_clip.clicked.connect(self.video_clip)

    def extract_frame(self):
        self.fps = self.ui.comboBox_fps.currentText()  # 获取fps
        from fractions import Fraction  # str转float
        self.fps = float(Fraction(self.fps))
        self.input_path = self.ui.lineEdit_file_path.text()
        self.output_path = self.ui.comboBox_output.currentText()  # 获取图片存储路径

        print('Input Path: ', self.input_path)
        print('Output path: ', self.output_path)
        print('FPS: ', self.fps)

        if os.path.isdir(self.input_path):
            extract_videos_frame(self.input_path, self.fps, self.output_path)
        else:
            extract_videos_frame(self.input_path, self.fps, self.output_path)

    def video_clip(self):
        self.input_path = self.ui.lineEdit_file_path.text()
        self.start = int(self.ui.lineEdit_start.text())
        self.duration = int(self.ui.lineEdit_duration.text())

        print('Input Path: ', self.input_path)
        print('Start time: %d s' % self.start)
        print('Duration : %d s' % self.duration)
        if not os.path.isdir(self.input_path):
            video_clip(self.input_path, self.start, self.duration)


if __name__ == '__main__':
    app = QApplication([])    # 启动一个应用
    window = MainWindow()     # 实例化主窗口
    window.show()             # 展示主窗口
    app.exec()                # 避免程序执行到这一行后直接退出
视频处理代码:ffmpeg.py
import shutil
import hashlib
import os


def get_md5(url):
    if isinstance(url, str):
        url = url.encode("utf-8")
    md = hashlib.md5()
    md.update(url)
    return md.hexdigest()


def extract_video_frame(video_path, fps, output_path):
    # 创建存储图片的文件夹
    video_name = os.path.split(video_path)[1].split('.')[0].replace(' ', "")
    print(video_name)
    if output_path == "Images":
        output_path = os.path.join(os.path.dirname(video_path), "Images")
    else:
        output_path = os.path.join(os.path.dirname(video_path), video_name)
        if os.path.exists(output_path):
            shutil.rmtree(output_path)
    if not os.path.exists(output_path):
        os.makedirs(output_path)

    # ffmpeg把视频转图片
    hash_name = get_md5(video_path)
    image_name = hash_name + "-" + video_name
    commend = "ffmpeg -i \"%s\" -vf fps=%f -q:v 1  %s\%s-%%4d.jpg" % \
              (video_path, fps, output_path, image_name)
    # 执行cmd命令
    os.system(commend)
    print("Commend: ", commend)


def extract_videos_frame(videos_dir, fps, output_path):
    # 对该目录下所有文件抽帧
    videos_path = []
    for root, dirs, files in os.walk(videos_dir):
        videos_path.extend([os.path.join(root, name) for name in files if name[-4:] in ["webm", ".mp4", ".MP4"]])
    print(videos_path)
    for video_path in videos_path:
        extract_video_frame(video_path, fps, output_path)


def video_clip(video_path, start, duration):
    new_video_name = os.path.split(video_path)[1][0:-4]+'_cliped'+os.path.split(video_path)[1][-4:]
    save_path = os.path.join(os.path.split(video_path)[0], new_video_name)
    commend = 'ffmpeg -i %s -ss %d -t %d -c copy %s' % (video_path, start, duration, save_path)
    os.system(commend)


QLineEditEX.py

其中,拖拽文件获取文件路径,网络大都是pyside2版的教程。


https://www.2bboy.com/archives/173.html

该博客评论区下的“喵喵”,提供了pyside6下实现“拖拽文件获取路径”的解决方案。


具体做法是:右击QLineEdit标签“提升为”,如下图进行设置。


接着创建文件QLineEditEX.py ,继承QLineEdit并重写拖拽功能。



from PySide6.QtWidgets import QLineEdit

class MyQLine(QLineEdit):
    """实现文件拖放功能"""
    def __init__(self, parent=None):
        super().__init__(parent)
        self.setAcceptDrops(True)

    def dragEnterEvent(self, event):
        data = event.mimeData()
        urls = data.urls()
        if (urls and urls[0].scheme() == 'file'):
            event.acceptProposedAction()

    def dragMoveEvent(self, event):
        data = event.mimeData()
        urls = data.urls()
        if urls and urls[0].scheme() == 'file':
            event.acceptProposedAction()

    def dropEvent(self, event):
        data = event.mimeData()
        urls = data.urls()
        if urls and urls[0].scheme() == 'file':
            # for some reason, this doubles up the intro slash
            filepath = str(urls[0].path())[1:]
            self.setText(filepath)
结果

可以拖拽视频文件实现路径的获取,处理单个视频。


也可以直接输入文件目录,处理文件下所有视频文件。


欢迎分享,转载请注明来源:内存溢出

原文地址: https://outofmemory.cn/langs/567787.html

(0)
打赏 微信扫一扫 微信扫一扫 支付宝扫一扫 支付宝扫一扫
上一篇 2022-04-09
下一篇 2022-04-09

发表评论

登录后才能评论

评论列表(0条)

保存