一、简要说明
  • 简述:本文主要展示将视频转成ASCII符号形式展示出来,带音频。
  • 运行环境:Win10/Python3.5。
  • 主要模块: PILnumpyshutil
二、简单分析

  在网上看到转成字符形式的视频,感觉挺有趣的,于是查阅相关资料,开始实现一下。基本思路:主要使用 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] 将视频转成ASCII符号形式、生成GIF图片的更多相关文章

  1. Python下字符画(ascii art)生成

    之前在b站上看到有人用C写了个脚本把妹抖龙op转换成字符画的形式输出了,感觉比较好玩在下就用python也写了一遍(主要是因为python比较简单好用).这里就这里就不介绍字符画了,因为能搜到这个的肯 ...

  2. 超不清视频播放器-用Python将视频转成字符

    前言 今天分享的这段代码,看起来没啥实际用处,而且有些反潮流,因为现如今大家看视频都追求更高分辨率的超清画质,而我们这个,是一个“超不清”的视频播放器:在控制台里播放视频,用字符来表示画面 不过我觉得 ...

  3. python 将视频转换成音频

    安装库 sudo pip install moviepy 代码 index.py from moviepy.editor import * video = VideoFileClip('test.mp ...

  4. 音视频入门-13-使用开源库生成PNG图片

    * 音视频入门文章目录 * RGB-to-PNG 回顾 上一篇 [手动生成一张PNG图片] 根据 [PNG文件格式详解] 一步一步地手动实现了将 RGB 数据生成了一张 PNG 图片. 有许多开源的 ...

  5. python 视频转成代码视频

    # -*- coding:utf-8 -*- # coding:utf-8 import os, cv2, subprocess, shutil from cv2 import VideoWriter ...

  6. Python如何把八进制转换成ASCII码

    做题途中拿到一串八进制字符串 0126 062 0126 0163 0142 0103 0102 0153 0142 062 065 0154 0111 0121 0157 0113 0111 010 ...

  7. Python远程视频监控

    Python远程视频监控程序   老板由于事务繁忙无法经常亲临教研室,于是让我搞个监控系统,让他在办公室就能看到教研室来了多少人.o(>﹏<)o||| 最初我的想法是直接去网上下个软件,可 ...

  8. [原创]使用python对视频/音频文件进行详细信息采集,并进行去重操作

    [原创]使用python对视频/音频文件进行详细信息采集,并进行去重操作 转载请注明出处 一.关于为什么用pymediainfo以及pymediainfo的安装 使用python对视频/音频文件进行详 ...

  9. Python学习教程(Python学习视频_Python学些路线):Day06 函数和模块的使用

    Python学习教程(Python学习视频_Python学些路线):函数和模块的使用 在讲解本章节的内容之前,我们先来研究一道数学题,请说出下面的方程有多少组正整数解. $$x_1 + x_2 + x ...

随机推荐

  1. C#编程.面向对象编程.可删除对象(Using{})

    Using关键字可以在代码块中初始化使用重要资源的对象,Dispose()方法会在这个代码块的末尾自动调用,用法如下: <ClassName> <VariableName> = ...

  2. java练习---7

    //程序员:罗元昊 2017.10.7 import java.util.Scanner; public class L { public static void main(String[] args ...

  3. 一个完整的产品设计流程——家庭安全管家

    不管是产品设计,还是前后端开发,始终都应该做出来才能够有很好的提高锻炼.书看得再多,如果不配合实际练习始终得不到实质性的进展. 接下来的案例是和几位学弟学妹一起做的,契机是参加一个用户体验设计比赛,从 ...

  4. MyBatis在Spring环境下的事务管理

    MyBatis的设计思想很简单,可以看做是对JDBC的一次封装,并提供强大的动态SQL映射功能.但是由于它本身也有一些缓存.事务管理等功能,所以实际使用中还是会碰到一些问题--另外,最近接触了JFin ...

  5. Git-命令行-使用 git stash 暂存代码

    为什么我们需要它不得不说,在知道这个命令的时,以及之后的使用中,我都超级热爱这个命令,因为它真的太好用了. 给大家说一下我使用这个命令的场景: 此时我在 feature_666 分支,非常聚精会神加持 ...

  6. 隐马尔科夫模型HMM介绍

    马尔科夫链是描述状态转换的随机过程,该过程具备“无记忆”的性质:即当前时刻$t$的状态$s_t$的概率分布只由前一时刻$t-1$的状态$s_{t-1}$决定,与时间序列中$t-1$时刻之前的状态无关. ...

  7. 章节十五、5-记录日志---Log4j

    一.为什么要用Log4j记录日志? 日志记录对于任何应用程序都非常重要. 它可以帮助我们快速调试代码,通过收集代码执行的信息让代码容易维护. 二.Log4j 是什么? Apache为Java提供的日志 ...

  8. 对数变换(一些基本的灰度变换函数)基本原理及Python实现

    1. 基本原理 变换形式如下 $$T(r) = c\lg(r+1)$$ c为常数 由于对数函数的导数随自变量的增大而减小,对数变换将输入窄范围的低灰度值扩展为范围宽的灰度值和宽范围的高灰度值压缩为映射 ...

  9. 微信公众平台注册及AppID和AppSecret的获取

    一.注册公众平台 1.入口 浏览器搜索“微信公众平台”,进入官网,点右上角立即注册. 2.选择账号类型 注册前需要选择一个账号类型,共有4个账号类型可以选择,每种类型能提供不同的功能,功能区别见下图. ...

  10. 基于RobotFramework实现自动化测试

    Java + robotframework + seleniumlibrary 使用Robot Framework Maven Plugin(http://robotframework.org/Mav ...