Python+tkinter+opencv2制作一个视频切帧小工具带GUI

文章目录

  • 前言
  • 一、基础知识
  • 二、开发环境
  • 三、正文
    • 1.视频切帧核心代码
    • 2.编写GUI主类
    • 3、主页面设计
    • 4、路径选择
    • 5、处理完成继续处理操作
      • 6、难点:进度条的制作
      • 7、操作演示
  • 四、利用Pyinstaller打包成exe
  • 总结
      • 软件获取


前言

前不久做数字图像处理的时候用到了视频切帧,简单来说就是把视频分为一帧一帧的图片,这几天正好有空,我便在此基础上结合图形化界面对其进行了封装,最后还利用pythinstaller将其打包成了exe可执行文件。
界面如下:
Python+tkinter+opencv2制作一个视频切帧小工具带GUI_第1张图片


一、基础知识

帧率:FPS(每秒钟要多少帧画面); 以及Gop(表示多少秒一个I帧)

码率:编码器每秒编出的数据大小,单位是kbps,比如800kbps代表编码器每秒产生800kb(或100KB)的数据。

分辨率:单位英寸中所包含的像素点数; VGA:Video Graphics Array(视频图像分辨率)

由于这也不是我们的重点,有需要的小伙伴可以自行百度。

二、开发环境

python 3.9.13(其它和opencv兼容的版本也行)
PyCharm 2022.1.4 Professional Edition(python宇宙最强大编辑器,哈哈哈哈)
opencv-python 4.5.5.64
安装什么的也就不多说了,默认大家已经装过了。

三、正文

整体代码呢也是非常的简洁,相关的都封装在类里面了。

1.视频切帧核心代码

关于视频切帧的代码网上有很多,如果你想追求效率还可以使用ffmpge版本的,不过我使用之后感觉兼容性可能不是那么的好,所以我采用的是opencv-python自带的,比较简便和简洁。

代码如下:
videoprocess.py

def video2frame(video_path, frame_save_path, time_interval=1):
    """
    :param video_path: 视频存放地址
    :param frame_save_path: 生成图片存放地址
    :param time_interval:  帧数的间隔,也就是说隔几帧去一次,默认为1
    """
    vidcap = cv2.VideoCapture(video_path)
    success, image = vidcap.read()
    count = 0
    frame_count = vidcap.get(7)
    print("当前要切割视频共有 %d 帧" % frame_count)  # 获取视频总帧数
    while success:
        success, image = vidcap.read()
        count += 1
        if count % time_interval == 0:
            cv2.imencode('.jpg', image)[1].tofile(frame_save_path + "/frame%d.jpg" % count)
        # from skimage import io
        # name = frame_save_path.join("/frame%d.jpg" % count)
        # io.imread(name)
        if count == frame_count - 1:
            print(vidcap.isOpened())
            break
        print(count)
    vidcap.release()

2.编写GUI主类

代码如下:

class MyGui:
    """主类"""
    def __init__(self, init_window_name):
        self.button = None
        self.init_window_name = init_window_name
        self.video_path = tk.StringVar()  # 视频路径
        self.image_save_path = tk.StringVar()  # 图片存储路径
        self.x = tk.StringVar()  # 进度条的值
        self.go_on = True

该处使用的url网络请求的数据。


3、主页面设计

    def set_init_window(self):
        self.init_window_name.title("视频切帧")  # 窗口标题栏
        self.center_window(360, 300)  # 窗口大小为360x300
        self.init_window_name.resizable(width=tk.FALSE, height=tk.FALSE)  # 限制窗口大小,拒绝用户调整边框大小
        tk.Label(self.init_window_name, text="欢迎使用视频切帧小工具",
                 bg="SkyBlue", fg="Gray").place(x=100, y=10)

        tk.Button(self.init_window_name, text="关于", relief='flat',
                  bg="SkyBlue", command=self.regarding).place(x=330, y=1)

        # 标签组件,用来显示内容,place里的x、y是标签组件放置的位置
        tk.Label(self.init_window_name, text="视频加载路径:",
                 bg="SkyBlue").place(x=10, y=50)  # 标签组件,用来显示内容,place里的x、y是标签组件放置的位置
        tk.Label(self.init_window_name, text="图片存放路径:",
                 bg="SkyBlue").place(x=10, y=90)  # 标签组件,用来显示内容,place里的x、y是标签组件放置的位置

        tk.Entry(self.init_window_name, textvariable=self.video_path).place(x=95, y=50)  # 输入控件,用于显示简单的文本内容
        tk.Entry(self.init_window_name, textvariable=self.image_save_path).place(x=95, y=90)  # 输入控件,用于显示简单的文本内容
        tk.Button(self.init_window_name, text="选择路径", width=10, height=1,
                  command=self.select_video_path, bg="SkyBlue").place(x=260, y=45)
        tk.Button(self.init_window_name, text="选择路径", width=10, height=1,
                  command=self.select_image_dir, bg="SkyBlue").place(x=260, y=85)

        self.button = tk.Button(self.init_window_name, text="开始执行", bg="SkyBlue",
                                activebackground="red", name="start",
                                state=tk.DISABLED,
                                command=self.option)
        self.button.place(width=200, height=50, x=80, y=150)

        # 按钮组件,用来触发某个功能或函数
        self.init_window_name["bg"] = "SkyBlue"  # 窗口背景色
        self.init_window_name.attributes("-alpha", 1)  # 虚化,值越小虚化程度越高

        global TimeLabel  # 全局变量
        # 标签组件,显示时间
        TimeLabel = tk.Label(text="%s%d" % (
            datetime.datetime.now().strftime('%Y-%m-%d %H:%M:%S:'),
            datetime.datetime.now().microsecond // 100000), bg="SkyBlue")

        TimeLabel.place(x=100, y=260)
        self.init_window_name.after(1000, self.uptime)

        # 进度条
        tk.Label(self.init_window_name, text='处理进度:', bg="SkyBlue").place(x=10, y=225)
        self.x.set(str("0.0 %"))
        ttk.Label(self.init_window_name, textvariable=self.x, width=7, ).place(x=290, y=226)

        self.mpb = ttk.Progressbar(self.init_window_name, orient="horizontal",
                                   length=200, mode="determinate", cursor='spider')
        self.mpb.place(x=80, y=225)

4、路径选择

    def select_video_path(self):  # 选择视频文件路径
        path_ = askopenfilename(title="请选择待处理视频", initialdir="./",
                                filetypes=[("*.MP4", "*.mp4")])
        if not path_:
            tk.messagebox.showinfo(title="提示", message="未选择路径!")
        else:
            # print(path_)
            self.video_path.set(path_)

    def select_image_dir(self):
        dirname = askdirectory(title="请选择图片存放位置", initialdir="./")  # 打开文件夹对话框
        if not dirname:
            messagebox.showwarning('警告', message='未选择文件夹!')  # 弹出消息提示框
        else:
            # print(dirname)
            self.image_save_path.set(dirname)
            self.button.config(state=tk.NORMAL)

5、处理完成继续处理操作

    def restart(self):
        """是否继续执行"""
        self.button.config(text='处理完成!', fg='red',
                           command="", relief="flat", font="25",
                           activebackground="lightblue")
        if not self.go_on:
            choice = messagebox.askyesno(title="处理完成", message="是否继续处理下一个视频?")
            if choice:  # 清空进度条
                self.mpb.stop()
                self.x.set(str("0.0 %"))
                self.button.config(state=tk.DISABLED, text="开始执行", bg="SkyBlue",
                                   activebackground="red", relief="groove")
            else:
                self.button.config(text='感谢你的使用!!', fg='red',
                                   command="", relief="flat", font="25",
                                   activebackground="skyblue")

6、难点:进度条的制作

进度条这里的确花费了我一些时间,由于对ttk组件的不熟悉,最后呢也是参考下面的代码完成了进度条的制作,用到了tkinter.ttk.Progressbar()最简单。

import time
import tkinter.ttk


def show():
    for i in range(100):
        # 每次更新加1
        progressbarOne['value'] = i + 1
        # 更新画面
        root.update()
        time.sleep(0.05)

if __name__ == '__main__':  
    root = tkinter.Tk()
    root.geometry('150x120')
    progressbarOne = tkinter.ttk.Progressbar(root)
    progressbarOne.pack(pady=20)
    # 进度值最大值
    progressbarOne['maximum'] = 100
    # 进度值初始值
    progressbarOne['value'] = 0
    button = tkinter.Button(root, text='Running', command=show)
    button.pack(pady=5)
    
    root.mainloop()

7、操作演示


(这模糊的图片,真令人难受…)

四、利用Pyinstaller打包成exe

python的可移植性比起java、C、C++什么的真的是有点差,但是也没办法,起码还是有能用的库,目前比较主流的是pyinstaller和 nuitka 什么的,打包命令如下:

pythoninstaller -F -w -i img.ico --clean 视频切帧.py

各参数说明:
-D	生成one-folder的程序(默认)	生成结果是一个目录,各种第三方依赖、资源和exe同时存储在该目录
-F	生成one-file的程序	生成结果是一个exe文件,所有的第三方依赖、资源和代码均被打包进该exe内
-c	显示命令行窗口	与-w相反,默认含有此参数
-w	不显示命令行窗口	编写GUI程序时使用此参数有用。
-i	为jisuanq.exe指定图标	pyinstaller -i beauty.ico jisuanq.py
--clean 清除多余的生成文件

最终打包成的exe还是有54m,略大,python写脚本还行,打包一言难尽哎。。。
感兴趣的也可以试试nuitka,最近才接触到的,用起来还是非常的不错,关键是比pyinstaller打包的文件小,随随便便50M+起步是真的难受。。。

nuitka --mingw64 --onefile --plugin-enable=upx --windows-icon-from-ico=img.ico --output-dir=out 
--windows-disable-console --show-memory --nofollow-imports --include-package=cv2 --recurse-not-to=numpy,jinja2--show-progress 视频切帧.py

各参数解析可以参考:
https://zhuanlan.zhihu.com/p/165978688
https://www.misaraty.com/2022-02-08_python%E6%89%93%E5%8C%85#nuitka
此外还有 auto-py-to-exe傻瓜式图形化界面打包也可以试试
Python+tkinter+opencv2制作一个视频切帧小工具带GUI_第2张图片

总结

虽然有点曲折,但终究还是完成了,与爬虫结合,从此妈妈再也不用担心有我截不下来的图片了!

软件获取

蓝奏云 (提取码ei6k)
https://yxn4065.lanzouq.com/iYiLQ08ck16d?password=ei6k
![在这里插入图片描述](https://img-blog.csdnimg.cn/5a11eb2f51544e138afaed40805674a9.png

你可能感兴趣的:(python,音视频,opencv,计算机视觉)