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

  在网上看到转成字符形式的视频,感觉挺有趣的,于是查阅相关资料,开始实现一下。基本思路:主要使用 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. zabbix3.4汉化

    1.管理员用户登入zabbix页面,更改语言为Chinese(zh_CN),点击Update 2.解决zabbix页面中文乱码 2.1在windows的C:\Windows\Fonts找到字体文件si ...

  2. 数据结构与算法基础之malloc()动态分配内存概述

    动态内存分配和释放: 动态构造一维数组: 假设动态构造一个Int型数组: int *p = (int *)malloc(int len); //还可以写作: int *p = (int *)mallo ...

  3. web设计_3_可伸缩的导航栏

    1. HTML5构建一个选项卡,需要<nav>标签包围一个无序列表,也可以添加role属性告诉辅助设备(如屏幕阅读器)这个元素所扮演的角色. 绝对不要基于图片的导航,对搜索引擎不友好,更新 ...

  4. C++学习之路

    一.二分查找 1.binary_search:查找某个元素是否出现. a.函数模板:binary_search(arr,arr+size ,indx) b.参数说明: arr: 数组首地址 size: ...

  5. 【Machine Learning·机器学习】决策树之ID3算法(Iterative Dichotomiser 3)

    目录 1.什么是决策树 2.如何构造一棵决策树? 2.1.基本方法 2.2.评价标准是什么/如何量化评价一个特征的好坏? 2.3.信息熵.信息增益的计算 2.4.决策树构建方法 3.算法总结 @ 1. ...

  6. <<Modern CMake>> 翻译 2.4 项目目录结构

    <<Modern CMake>> 翻译 2.4 项目目录结构 本节内容有点跑题.但我认为这是一个很好的方法. 我将告诉你如何规划项目的目录. 这是基于惯例,但将帮助您: 轻松阅 ...

  7. 后端小白的VUE入门笔记, 前端高能慎入

    因为项目需要前后端分离,后端竟然不用控制view层了,页面的跳转后端不再干涉,(前端的vue经过打包后成了一张index.html) 后端只需要响应给前端json串就ok,其实这不是爽歪歪?但是觉得还 ...

  8. luogu1220_关路灯 区间dp

    传送门 区间dp f[i][j][state] : [i, j]区间 state=0 当前选i state = 1 当前选j 注意枚举的顺序 转移的设计时 在同时刻不在[i,j]区间里的数也要考虑 不 ...

  9. LeetCode 85. 冗余连接 II

    题目: 在本问题中,有根树指满足以下条件的有向图.该树只有一个根节点,所有其他节点都是该根节点的后继.每一个节点只有一个父节点,除了根节点没有父节点. 输入一个有向图,该图由一个有着N个节点 (节点值 ...

  10. Ubuntu 10.04下实现双网卡负载均衡

    摘要:本文主要介绍和配置 在Ubuntu下 实现 bonding,双网卡负载,bonding模式为0,好处是负载平衡,另一网卡断了,也能工作. 什么是bonding Linux bonding 驱动提 ...