如何使用Python编写代码来将视频转换为高清动态图片,从而无需依赖费用高昂的第三方工具或忍受低质量的输出呢?
具体的实现可以参考本篇博文,这个技术对于制作演示文稿、社交媒体内容或博客文章都非常有用,因为它可以让你轻松地将视频片段转换为清晰的GIF动画。
最近,我需要为一次PPT答辩准备一些动态图片,而这些图片需要从一个视频中提取。我尝试了一些在线工具和付费软件,但发现要么转换结果模糊不清,要么带有水印,要么需要付费。于是,我决定自己动手写代码来实现这个任务。
在开始之前,让我们先了解一下我们将使用的技术栈:
- Python:作为编程语言的选择,Python是一种功能强大且易于使用的语言,适合处理图像和视频。
- OpenCV:这是一个广泛用于计算机视觉任务的库,我们将使用它来处理视频帧。
- imageio:这个库将帮助我们读取视频文件并将帧保存为GIF。
下面是将视频转换为高清动态图片的主要步骤:
1. 安装所需库:
使用pip或conda安装所需的Python库:
pip install opencv-python imageio
整个过程我分为三个板块进行实现:
通过VideoCapture读取视频,然后imwrite保存图片。代码如下:
- def video_to_image(videoPath, imgPath, save_format=".jpg", imgNumber=0, nameLength=4):
- """
- params:
- videoPath : 视频路径 例如 u'E:\Test/123.mp4'
- imgPath : 图片路径 例如 r'F:\Test/tu/' #保存图片路径,路径最后加/斜杠
- save_format : 保存的图片格式,默认为jpg可以改为'.png'
- imgNumber : 图片保存的名字数量默认为0开始
- """
- print("视频转图片的方法")
- if imgPath[-1] != "/":
- imgPath + "/"
- capture = cv2.VideoCapture(videoPath)
- frame_num = int(capture.get(cv2.CAP_PROP_FRAME_COUNT))
- suc = capture.isOpened() # 是否成功打开
- frame_count = imgNumber # 图片张数从多少开始
- while suc:
- try:
- frame_count += 1
- suc, frame = capture.read()
- cv2.imwrite(imgPath + str(frame_count).zfill(4) + save_format, frame)
- # zfill() 方法返回指定长度的字符串,原字符串右对齐,前面填充0
- #:str.zfill(width) 参数width
- if frame_num == frame_count:
- suc = False
- except:
- break
- capture.release()
- print("视频转图片结束! ")
用到的imageio.get_writer作为gif文件流写入图片,
imageio.get_writer(uri = gifname,mode = "I",fps = 25)
其中这三个参数分别表示:
uri:合成后的gif动图的名字,可以随意更改。 mode:操作模式,I表示多图,不用更改。 fps:帧率,也就是画面每秒传输帧数,值越大,gif动图的播放速度越大。
代码如下:
- def image_to_gif(st_imagename=1, end_imagename=1, gifname="demo.gif"):
- import imageio
-
- """
- uri:合成后的gif动图的名字,可以随意更改。
- mode:操作模式,I表示多图,不用更改。
- fps:帧率,也就是画面每秒传输帧数,值越大,gif动图的播放速度越大。
- gifname:保存的gif名字
- """
- os.chdir(os.getcwd())
- if not os.path.exists("images"):
- os.makedirs("images")
-
- with imageio.get_writer(uri=gifname, mode="I", fps=25) as writer:
- for i in range(st_imagename, end_imagename + 1): # 选择多少张
- print(f"正在处理第{i}张")
- filename = str(i).zfill(4)
- file_path = os.path.join(os.getcwd(), "images", filename)
- writer.append_data(imageio.imread(file_path + ".jpg"))
以下是第三步的代码
- def video_to_gif(videoPath, resultName, fps=25):
- """
- :param videoPath: 视频路径 绝对路径。且路径不要含中文
- :param resultName: 输出的gif文件名
- :param fps: 保存的gif帧率,fps默认为25
- """
- sample = [chr(i) for i in range(97, 97 + 26)] + [str(i) for i in range(10)]
- random_name = "_" + "".join(random.sample(sample, 10)) # 用于临时存储数据图片
- # shutil.rmtree(file_path) #删除
- if not os.path.exists(random_name):
- os.makedirs(random_name)
- tmp_path = os.path.join(os.getcwd(), random_name) + "/"
- print("step 1 loading:video_to_image start")
- video_to_image(videoPath, tmp_path) # 视频转化完毕
- print("step 1 finished:video_to_image end")
- image_nums = len(os.listdir(tmp_path))
- image_nums = (
- 375 if image_nums > 375 else image_nums
- ) # TODO 默认上限为15s 即:25 * 15 = 375张照片
- print("step 2 loading:image_to_gif start")
- image_to_gif(st_imagename=1, end_imagename=image_nums, gifname=resultName)
- print("step 2 finished:image_to_gif end")
- shutil.rmtree(random_name)
- print("All Finished")
有需要的兄弟姐们可以前往Fork、star一下,,欢迎大家加入本项目一起为开源做贡献!~
下面是整个项目代码:
- import string
- import imageio
- import os
- import cv2
- import shutil
- import base64
- import random
-
-
- def video_to_image(videoPath, imgPath, save_format=".jpg", imgNumber=0, nameLength=4):
- """
- params:
- videoPath : 视频路径 例如 u'E:\Test/123.mp4'
- imgPath : 图片路径 例如 r'F:\Test/tu/' #保存图片路径,路径最后加/斜杠
- save_format : 保存的图片格式,默认为jpg可以改为'.png'
- imgNumber : 图片保存的名字数量默认为0开始
- """
- print("视频转图片的方法")
- if imgPath[-1] != "/":
- imgPath + "/"
- capture = cv2.VideoCapture(videoPath)
- frame_num = int(capture.get(cv2.CAP_PROP_FRAME_COUNT))
- suc = capture.isOpened() # 是否成功打开
- frame_count = imgNumber # 图片张数从多少开始
- while suc:
- try:
- frame_count += 1
- suc, frame = capture.read()
- cv2.imwrite(imgPath + str(frame_count).zfill(4) + save_format, frame)
- # zfill() 方法返回指定长度的字符串,原字符串右对齐,前面填充0
- #:str.zfill(width) 参数width
- if frame_num == frame_count:
- suc = False
- except:
- break
- capture.release()
- print("视频转图片结束! ")
-
-
- def image_to_gif(file_path, st_imagename=1, end_imagename=1, gifname="demo.gif"):
- import imageio
-
- """
- uri:合成后的gif动图的名字,可以随意更改。
- mode:操作模式,I表示多图,不用更改。
- fps:帧率,也就是画面每秒传输帧数,值越大,gif动图的播放速度越大。
- gifname:保存的gif名字
- """
- # os.chdir(os.getcwd())
- # if not os.path.exists("images"):
- # os.makedirs("images")
-
- with imageio.get_writer(uri=gifname, mode="I", fps=25) as writer:
- for i in range(st_imagename, end_imagename + 1): # 选择多少张
- print(f"正在处理第{i}张")
- filename = str(i).zfill(4)
- # file_path = os.path.join(os.getcwd(),'images',filename)
- file_path2 = file_path + filename + ".jpg"
- writer.append_data(imageio.imread(file_path2))
-
-
- def video_to_gif(videoPath, resultName, fps=25):
- """
- :param videoPath: 视频路径 绝对路径。且路径不要含中文
- :param resultName: 输出的gif文件名
- :param fps: 保存的gif帧率,fps默认为25
- """
- sample = [chr(i) for i in range(97, 97 + 26)] + [str(i) for i in range(10)]
- random_name = "_" + "".join(random.sample(sample, 10)) # 用于临时存储数据图片
- # shutil.rmtree(file_path) #删除
- if not os.path.exists(random_name):
- os.makedirs(random_name)
- tmp_path = os.path.join(os.getcwd(), random_name) + "/"
- print("step 1 loading:video_to_image start")
- video_to_image(videoPath, tmp_path) # 视频转化完毕
- print("step 1 finished:video_to_image end")
- image_nums = len(os.listdir(tmp_path))
- image_nums = (
- 375 if image_nums > 375 else image_nums
- ) # TODO 默认上限为15s 即:25 * 15 = 375张照片
- print("step 2 loading:image_to_gif start")
- image_to_gif(tmp_path, st_imagename=1, end_imagename=image_nums, gifname=resultName)
- print("step 2 finished:image_to_gif end")
- shutil.rmtree(random_name)
- print("All Finished")
-
-
- if __name__ == "__main__":
- video_to_gif(r"E:\Python_tools\Python_tools\video\test.mp4", "test1.gif")