热门标签 | HotTags
当前位置:  开发笔记 > 编程语言 > 正文

[Python]将视频转成ASCII符号形式、生成GIF图片

一、简要说明简述:本文主要展示将视频转成ASCII符号形式展示出来,带音频。运行环境:Win10Python3.5。主要模块:PIL、numpy、shutil。注意点:ffmpeg








一、简要说明


  • 简述:本文主要展示将视频转成ASCII符号形式展示出来,带音频。

  • 运行环境:Win10/Python3.5。

  • 主要模块: PIL、numpy、shutil。



  • 注意点:ffmpeg.exe(视频处理) 可以自行网上下载。

  • 本文主要参考:Python将视频转换为全字符视频(含音频)








二、简单分析

  在网上看到转成字符形式的视频,感觉挺有趣的,于是查阅相关资料,开始实现一下。基本思路:主要使用 ffmpeg 对进行视频操作,然后使用 PIL 对图片进行缩小、灰度和转码的处理。流程如下:


1. 创建临时路径。
2. 将视频按帧分割成图片存入临时目录。
3. 遍历将图片缩放、转成灰度,再转成ASCII形式的图片。
4. 将ASCII形式的图片合成视频。
5. 获取源文件的音频文件。
6. 合并视频和音频文件。


  再来看看效果图:

   









三、开发流程

  3.1、创建目录,存储图片的临时路径

# [1]、创建存储临时图片的路径
def createpath(self):
print("-" * 30)
print("[1/6]正在创建临时路径...")
print("-" * 30 + \'\r\n\')
# 源视频文件的图片路径
if not os.path.exists(self.pic_path):
os.makedirs(self.pic_path)
else:
# 清空在创建
shutil.rmtree(self.pic_path)
os.makedirs(self.pic_path)
# 转换之后的图片路径
if not os.path.exists(self.ascii_path):
os.makedirs(self.ascii_path)
else:
# 清空再创建
shutil.rmtree(self.ascii_path)
os.makedirs(self.ascii_path)

# 存储输出文件的目录
if not os.path.exists(self.outpath):
os.makedirs(self.outpath)

以上代码主要创建源视频切割图片存储路径、转码后图片存储路径和输出文件的存储路径,图片的存储路径为 临时路径 ,每次执行前会先清空之前的文件,请注意。


  3.2、将视频分割成图片

# [2]、将视频分割成图片
def video2pic(self):
print("-" * 30)
print("[2/6]正在切割原始视频为图片...")
print("-" * 30 + \'\r\n\')
# 使用ffmpeg切割图片,命令行如下
cmd = \'ffmpeg -i {0} -r 24 {1}/%06d.jpeg\'.format(self.filename, self.pic_path)
# 执行命令
os.system(cmd)

cmd:ffmpeg -i [输入文件名] -r [fps,帧率] [分割图存储路径]

这里就比较简单,使用 ffmpeg 将视频分割成图片并按照相应个数存储在临时路径即可。查阅ffmpeg命令行说明


  3.3、将视频分割成图片

# [3]、将图片缩放、转成ascii形式
def pic2ascii(self):
print("-" * 30)
print("[3/6]正在处理分析图片,转成ascii形式...")
print("-" * 30 + \'\r\n\')
# 读取原始图片目录
pic_list = sorted(os.listdir(self.pic_path))
total_len = len(pic_list)
count = 1
# 遍历每张图片
for pic in pic_list:
# 图片完整路径
imgpath = os.path.join(self.pic_path, pic)
# 1、缩小图片,转成灰度模式,存入数组
origin_img = Image.open(imgpath)
# 缩小之后宽高
resize_width = int(origin_img.size[0] / self.resize_times)
resize_height = int(origin_img.size[1] / self.resize_times)
resize_img = origin_img.resize((resize_width, resize_height), Image.ANTIALIAS).convert("L")
img_arr = np.array(resize_img)
# 2、新建空白图片(灰度模式、与原始图片等宽高)
new_img = Image.new("L", origin_img.size, 255)
draw_obj = ImageDraw.Draw(new_img)
fOnt= ImageFont.truetype("arial.ttf", 8)
# 3、将每个字符绘制在一定的区域内
for i in range(resize_height):
for j in range(resize_width):
x, y = j*self.resize_times, i*self.resize_times
index = int(img_arr[i][j]/4)
draw_obj.text((x, y), self.ascii_char[index], fOnt=font, fill=0)
# 4、保存字符图片
new_img.save(os.path.join(\'temp_ascii\', pic), "JPEG")
print("已生成ascii图(%d/%d)" % (count, total_len))
count += 1

这一步是重点,在遍历获取源图片目录列表之后,就可以分步进行操作了:



  1. 缩小图片、转成灰度模式,存入数组。

  2. 新建空白图片(灰度模式、与原始图片等宽高)。

  3. 将每个字符绘制在一定的区域内。

  4. 保存字符图片。

下面就是替换的字符:

self.ascii_char = list("$@B%8&WM#*oahkbdpqwO0QLCJYXzcvunxrjft/\|()1[]?-_+~<>i!......... ")

  3.4、将ascii形式的图片合成视频

# [4]、合成视频
def ascii2video(self):
print("-" * 30)
print("[4/6]正在合成视频...")
print("-" * 30 + \'\r\n\')
# 输出视频保存路径
savepath = os.path.join(self.outpath, self.outname)
cmd = \'ffmpeg -threads 2 -start_number 000001 -r 24 -i {0}/%06d.jpeg -vcodec mpeg4 {1}\'.format(self.ascii_path, savepath)
os.system(cmd)

遍历转码的图片,合成视频。

cmd:ffmpeg -threads 2 -start_number [开始图片编号] -r [帧率,fps] -i [图片路径] -vcodec [指定解码器] [输出文件名]

  3.5、获取音频mp3文件

# [5]、获取原始视频的mp3文件
def video2mp3(self):
print("-" * 30)
print("[5/6]正在分离音频文件...")
print("-" * 30 + \'\r\n\')
# mp3名字和保存路径
name = self.filename.split(\'.\')[0] + \'.mp3\'
savepath = os.path.join(self.outpath, name)
cmd = \'ffmpeg -i {0} -f mp3 {1}\'.format(self.filename, savepath)
os.system(cmd)

cmd:ffmpeg -i [输入视频文件名] -f mp3 [输出的mp3文件名]

  3.5、合并视频和音频文件

# [6]、将视频和音频合并
def mp4andmp3(self):
print("-"*30)
print("[6/6]正在合并视频和音频...")
print("-" * 30 + \'\r\n\')
cmd = \'ffmpeg -i {0} -i {1} -strict -2 -f mp4 {2}\'.format(self.mp4filename, self.mp3ilename, self.mergefilename)
os.system(cmd)

上面代码就是将视频和音频进行合并,转成全符号的视频也不会丢失音频。

cmd :ffmpeg -i [视频文件名] -i [音频文件名] -strict -2 -f mp4 [合并后的文件名]







四、生成GIF动图

# -*- coding:utf-8 -*-
import imageio
import os
# 图片路径
pic_path = "temp_pic"
# 输出文件名
outname = "jljt.gif"
# 越过的图片数
skip_num = 10
pic_list = sorted(os.listdir(pic_path))
frames = []
total_len = len(pic_list)
# 遍历、读取图片,这里的
for i in range(0, total_len, skip_num):
path = os.path.join(pic_path, pic_list[i])
frames.append(imageio.imread(path))
# 生成GIF图片
imageio.mimsave(outname, frames, "GIF", duration=0.1)
print("生成完成")

上面主要实现:将分割出来的图片,合成一张GIF动图,通过设置越过的图片数,可以减小容量,但是会加速动画效果,上面的效果图,就是通过这里生成的。








五、附录

*转发需注明出处

# -*- coding:utf-8 -*-
from PIL import Image, ImageDraw, ImageFont
import numpy as np
import os
import sys
import shutil
class Video2Ascii:
def __init__(self, filename):
# 执行前的一些判断
if not os.path.isfile(filename):
print("源文件找不到,或者不存在!")
exit()
temp_arr = filename.split(\'.\')
# 字符列表,从左至右逐渐变得稀疏,对应着颜色由深到浅
self.ascii_char = list("$@B%8&WM#*oahkbdpqwO0QLCJYXzcvunxrjft/\|()1[]?-_+~<>i!......... ")
# 传入视频文件名
self.filename = filename
# 输出视频文件名
self.outname = temp_arr[0] + "_out." + temp_arr[1]
# 存储图片的临时路径、输出路径
self.pic_path = \'temp_pic\'
self.ascii_path = \'temp_ascii\'
self.outpath = \'temp_out\'
# 设置图片缩小的倍数
self.resize_times = 6
# 设置输出文件的名字,声音文件以及带声音的输出文件
self.mp3ilename = os.path.join(self.outpath, temp_arr[0] + \'.mp3\')
self.mp4filename = os.path.join(self.outpath, self.outname)
# 合并输出的视频文件
self.mergefilename = os.path.join(self.outpath, temp_arr[0] + \'_voice.\' + temp_arr[1])
# [1]、创建存储临时图片的路径
def createpath(self):
print("-" * 30)
print("[1/6]正在创建临时路径...")
print("-" * 30 + \'\r\n\')
# 源视频文件的图片路径
if not os.path.exists(self.pic_path):
os.makedirs(self.pic_path)
else:
# 清空在创建
shutil.rmtree(self.pic_path)
os.makedirs(self.pic_path)
# 转换之后的图片路径
if not os.path.exists(self.ascii_path):
os.makedirs(self.ascii_path)
else:
# 清空再创建
shutil.rmtree(self.ascii_path)
os.makedirs(self.ascii_path)
# 存储输出文件的目录
if not os.path.exists(self.outpath):
os.makedirs(self.outpath)
# [2]、将视频分割成图片
def video2pic(self):
print("-" * 30)
print("[2/6]正在切割原始视频为图片...")
print("-" * 30 + \'\r\n\')
# 使用ffmpeg切割图片,命令行如下
cmd = \'ffmpeg -i {0} -r 24 {1}/%06d.jpeg\'.format(self.filename, self.pic_path)
# 执行命令
os.system(cmd)
# [3]、将图片缩放、转成ascii形式
def pic2ascii(self):
print("-" * 30)
print("[3/6]正在处理分析图片,转成ascii形式...")
print("-" * 30 + \'\r\n\')
# 读取原始图片目录
pic_list = sorted(os.listdir(self.pic_path))
total_len = len(pic_list)
count = 1
# 遍历每张图片
for pic in pic_list:
# 图片完整路径
imgpath = os.path.join(self.pic_path, pic)
# 1、缩小图片,转成灰度模式,存入数组
origin_img = Image.open(imgpath)
# 缩小之后宽高
resize_width = int(origin_img.size[0] / self.resize_times)
resize_height = int(origin_img.size[1] / self.resize_times)
resize_img = origin_img.resize((resize_width, resize_height), Image.ANTIALIAS).convert("L")
img_arr = np.array(resize_img)
# 2、新建空白图片(灰度模式、与原始图片等宽高)
new_img = Image.new("L", origin_img.size, 255)
draw_obj = ImageDraw.Draw(new_img)
fOnt= ImageFont.truetype("arial.ttf", 8)
# 3、将每个字符绘制在 8*8 的区域内
for i in range(resize_height):
for j in range(resize_width):
x, y = j*self.resize_times, i*self.resize_times
index = int(img_arr[i][j]/4)
draw_obj.text((x, y), self.ascii_char[index], fOnt=font, fill=0)
# 4、保存字符图片
new_img.save(os.path.join(\'temp_ascii\', pic), "JPEG")
print("已生成ascii图(%d/%d)" % (count, total_len))
count += 1
# exit()
# [4]、合成视频
def ascii2video(self):
print("-" * 30)
print("[4/6]正在合成视频...")
print("-" * 30 + \'\r\n\')
# 输出视频保存路径
savepath = os.path.join(self.outpath, self.outname)
cmd = \'ffmpeg -threads 2 -start_number 000001 -r 24 -i {0}/%06d.jpeg -vcodec mpeg4 {1}\'.format(self.ascii_path, savepath)
os.system(cmd)
# [5]、获取原始视频的mp3文件
def video2mp3(self):
print("-" * 30)
print("[5/6]正在分离音频文件...")
print("-" * 30 + \'\r\n\')
# mp3名字和保存路径
name = self.filename.split(\'.\')[0] + \'.mp3\'
savepath = os.path.join(self.outpath, name)
cmd = \'ffmpeg -i {0} -f mp3 {1}\'.format(self.filename, savepath)
os.system(cmd)
# [6]、将视频和音频合并
def mp4andmp3(self):
print("-"*30)
print("[6/6]正在合并视频和音频...")
print("-" * 30 + \'\r\n\')
cmd = \'ffmpeg -i {0} -i {1} -strict -2 -f mp4 {2}\'.format(self.mp4filename, self.mp3ilename, self.mergefilename)
os.system(cmd)
# [0]、启动
def start(self):
"""
> 程序流程:
1、创建路径
2、将原始视频分割成图片
3、将图片缩放、转成ascii形式
4、将ascii形式的图片合成视频
5、获取音频mp3文件
6、合并视频和音频文件
:return:
"""
self.createpath()
self.video2pic()
self.pic2ascii()
self.ascii2video()
self.video2mp3()
self.mp4andmp3()
print("程序执行完成")
if __name__ == "__main__":
if len(sys.argv) != 2:
print("参数不匹配,请参考(脚本名 原始视频):xxx.py test.mp4 ")
exit()
demo = Video2Ascii(sys.argv[1])
demo.start()



推荐阅读
  • 本文介绍了如何使用 Python 的 Pyglet 库加载并显示图像。Pyglet 是一个用于开发图形用户界面应用的强大工具,特别适用于游戏和多媒体项目。 ... [详细]
  • 使用R语言进行Foodmart数据的关联规则分析与可视化
    本文探讨了如何利用R语言中的arules和arulesViz包对Foodmart数据集进行关联规则的挖掘与可视化。文章首先介绍了数据集的基本情况,然后逐步展示了如何进行数据预处理、规则挖掘及结果的图形化呈现。 ... [详细]
  • 使用Python构建网页版图像编辑器
    本文详细介绍了一款基于Python开发的网页版图像编辑工具,具备多种图像处理功能,如黑白转换、铅笔素描效果等。 ... [详细]
  • 本文分享了作者在使用LaTeX过程中的几点心得,涵盖了从文档编辑、代码高亮、图形绘制到3D模型展示等多个方面的内容。适合希望深入了解LaTeX高级功能的用户。 ... [详细]
  • Hadoop集群搭建:实现SSH无密码登录
    本文介绍了如何在CentOS 7 64位操作系统环境下配置Hadoop集群中的SSH无密码登录,包括环境准备、用户创建、密钥生成及配置等步骤。 ... [详细]
  • 本文总结了在多人协作开发环境中使用 Git 时常见的问题及其解决方案,包括错误合并分支的处理、使用 SourceTree 查找问题提交、Git 自动生成的提交信息解释、删除远程仓库文件夹而不删除本地文件的方法、合并冲突时的注意事项以及如何将多个提交合并为一个。 ... [详细]
  • 本文探讨了一个Web工程项目的需求,即允许用户随时添加定时任务,并通过Quartz框架实现这些任务的自动化调度。文章将介绍如何设计任务表以存储任务信息和执行周期,以及如何通过一个定期扫描机制自动识别并加载新任务到调度系统中。 ... [详细]
  • 本文详细介绍了Apache Spark 2.2.0版本中集群模式的基本概念和工作流程,包括如何通过集群管理器分配资源,以及Spark应用程序在集群中的运行机制。链接:http://spark.apache.org/docs/2.2.0/cluster-overview.html ... [详细]
  • 通过命令行执行Robot Framework测试用例的方法
    本文介绍如何利用命令行工具来运行Robot Framework中的测试文件及用例,解决常见的模块未找到错误。 ... [详细]
  • 将XML数据迁移至Oracle Autonomous Data Warehouse (ADW)
    随着Oracle ADW的推出,数据迁移至ADW成为业界关注的焦点。特别是XML和JSON这类结构化数据的迁移需求日益增长。本文将通过一个实际案例,探讨如何高效地将XML数据迁移至ADW。 ... [详细]
  • 本文探讨了如何使用Scrapy框架构建高效的数据采集系统,以及如何通过异步处理技术提升数据存储的效率。同时,文章还介绍了针对不同网站采用的不同采集策略。 ... [详细]
  • 本文探讨了在已知最终数组尺寸不会超过5000x10的情况下,如何利用预分配和调整大小的方法来优化Numpy数组的创建过程,以提高性能并减少内存消耗。 ... [详细]
  • Python网络编程:深入探讨TCP粘包问题及解决方案
    本文详细探讨了TCP协议下的粘包现象及其产生的原因,并提供了通过自定义报头解决粘包问题的具体实现方案。同时,对比了TCP与UDP协议在数据传输上的不同特性。 ... [详细]
  • 本文介绍了使用Python和C语言编写程序来计算一个给定数值的平方根的方法。通过迭代算法,我们能够精确地得到所需的结果。 ... [详细]
  • 在Java开发中,如何利用ProcessBuilder类调用外部程序是一个常见的需求。本文将详细介绍ProcessBuilder类的使用方法,并提供示例代码帮助你更好地理解和应用。 ... [详细]
author-avatar
herozhx
这个家伙很懒,什么也没留下!
PHP1.CN | 中国最专业的PHP中文社区 | DevBox开发工具箱 | json解析格式化 |PHP资讯 | PHP教程 | 数据库技术 | 服务器技术 | 前端开发技术 | PHP框架 | 开发工具 | 在线工具
Copyright © 1998 - 2020 PHP1.CN. All Rights Reserved | 京公网安备 11010802041100号 | 京ICP备19059560号-4 | PHP1.CN 第一PHP社区 版权所有