前不久做数字图像处理的时候用到了视频切帧,简单来说就是把视频分为一帧一帧的图片,这几天正好有空,我便在此基础上结合图形化界面对其进行了封装,最后还利用pythinstaller将其打包成了exe可执行文件。
界面如下:
帧率: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
安装什么的也就不多说了,默认大家已经装过了。
整体代码呢也是非常的简洁,相关的都封装在类里面了。
关于视频切帧的代码网上有很多,如果你想追求效率还可以使用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()
代码如下:
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网络请求的数据。
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)
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)
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")
进度条这里的确花费了我一些时间,由于对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()
(这模糊的图片,真令人难受…)
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傻瓜式图形化界面打包也可以试试
虽然有点曲折,但终究还是完成了,与爬虫结合,从此妈妈再也不用担心有我截不下来的图片了!
蓝奏云 (提取码ei6k)
https://yxn4065.lanzouq.com/iYiLQ08ck16d?password=ei6k
![在这里插入图片描述](https://img-blog.csdnimg.cn/5a11eb2f51544e138afaed40805674a9.png