字符视频就是画面全部由字符组成的,那就先来看看效果:
![](https://img.haomeiwen.com/i14291617/4c7a27064042073e.png)
那么用代码怎么实现的呢?下面用python实现,话不多说,直接上干货。
我们都知道Python容易学,但是就是不知道如何去学,去哪里找资料,在这里呢,python学习交流qq群233539995,分享我精心准备的Python学习资料,0基础到进阶!希望你们在学习Python道路上少走弯路!加油!
代码实现详解
其实总体思路分为3个步骤:
1.将原视频分割成若干个图片以及分离出音频
2.将每张图片转为字符画图片(重点部分)
3.将若干个字符画图片和音频合并成新的视频(字符视频)
将原视频分割成若干个图片以及分离出音频
这个过程我们可以用python调用ffmpeg工具进行切割,ffmpeg是专门处理音视频的工具库。可以在ffmpeg官网下载可执行文件放在程序的当前目录
分离音频命令为:
ffmpeg.exe-ifilename-vntemp.mp3
#分离音频
slice_audio_cmd='ffmpeg.exe -i {0} -vn temp.mp3'.format(src_file)
os.system(slice_audio_cmd)
分割视频成若干图片的命令为:
ffmpeg.exe -i filename -r 24 temp_pic/%06d.jpeg
#切割成图片
slice_pic_cmd='ffmpeg.exe -i {0} -r 24 temp_pic/%06d.jpeg'.format(src_file)
os.system(slice_pic_cmd)
将分割出来的图片和音频临时存储起来,为了后面若干图片转字符图片效率及速度有所提高,还需将分割后的图片转为缩略图,就是改变图片的尺寸
这里使用python的PIL图形处理库来进行缩略图转化,同样将缩略图临时存储起来
def create_thumbnail(src_dir, dst_dir):
picts_list = sorted(os.listdir(src_dir))
forpictureinpicts_list:
base_name =os.path.basename(picture)
img = Image.open(os.path.join(src_dir, picture))
size =200,200
img.thumbnail(size, Image.ANTIALIAS)
img.save(os.path.join(dst_dir, base_name))
将每张图片转为字符画图片
如何将一张图片转为字符形式呢?其实很简单,分3步:
1.将图片转为灰度图
2.将灰度图的每个像素点替换为相应的字符
3.将所有替换后的字符画成一张字符图片
1.将图片转为灰度图
灰度图,Gray Scale Image 或是Grey Scale Image,又称灰阶图。把白色与黑色之间按对数关系分为若干等级,称为灰度。灰度分为256阶
公式为:Gray = R0.299 + G0.587 + B*0.114
同样在python中可以用PIL库直接转灰度:
defload_picture(filename):
# Gray = R*0.299 + G*0.587 + B*0.114
img = Image.open(filename).convert('L')
(x, y) = img.size
pixels = list(img.getdata())
img.close()
return(pixels, x, y)
2.将灰度图的每个像素点替换为相应的字符
这里如何替换呢?可以根据灰阶值来替换为我们自己设定的字符,例如:
symbols = list("$@B%8&WM#*oahkbdpqwmZO0QLCJUYXzcvunxrjft/\|()1{}[]?-_+~<>i!lI;:,\"^`'. ")
从上面列表可以看到:越靠前的越密集,越往后越稀疏,于是我们根据灰阶值的大小按比例取列表中的字符,灰阶值越大,取越靠后的字符,这样图片轮廓才能更好的清晰显示
为了是转化后的字符图片看起来不密集以及提高转化时间,我这里将每间隔1个像素来替换字符,初始还要指定图片的边框及尺寸,这些参数可以自行调整,具体展示如下代码:
defcreate_ascii_picture(pixels, symbols, dest_name, x_size, y_size):
scale
=4# 长宽扩大倍数
border =1# 边框宽度
interval_pixel =2#原图片间隔多少个像素点来填充
img = Image.new('L',
(x_size*scale +2*border,
y_size*scale +2*border),
255)
fnt = ImageFont.truetype('DejaVuSansMono.ttf',int(scale*3))
t = ImageDraw.Draw(img)
x = border
y =border
forjinrange(0, y_size, interval_pixel):
foriinrange(0, x_size, interval_pixel):
t.text( (x, y),
symbols[int(pixels[j*x_size + i]/256* len(symbols))],
font
=fnt,
fill=0
)
x += scale * interval_pixel
x = border
y += scale * interval_pixel
img.save(dest_name,"JPEG")
3.将所有替换后的字符画成一张字符图片
这步只需调用PIL库的save方法,如上面代码最后一行。
同样,我们将转化后的字符图片临时保存起来。
至此第2大步完成,即:将一张图片转为字符图片完成
将若干个字符画图片和音频合并成新的视频(字符视频)
这里也是使用ffmpeg工具进行合成,命令为:
ffmpeg-threads 2 -start_number 000001 -r 24 -i 路径名/%06d.jpeg -i temp.mp3 -vcodec mpeg4 生成的文件名
merge_ascii_video_cmd ='ffmpeg -threads 2 -start_number 000001 -r 24 -i {0}/%06d.jpeg -i temp.mp3 -vcodec mpeg4 {1}'.format('temp_ascii', dst_name)
os.system(merge_ascii_video_cmd)
这一步完成后,字符视频已经生成了。最后还需删除一些临时的文件及文件夹。
完整代码展示
from PIL import Image, ImageDraw, ImageFont
importos, sys
import shutil
symbols = list("$@B%8&WM#*oahkbdpqwmZO0QLCJUYXzcvunxrjft/\|()1{}[]?-_+~<>i!lI;:,\"^`'. ")
def ascii_art_convert(src_dir, dest_dir):
print('开始生成...')
picts_list = sorted(os.listdir(src_dir))
len_picts =len(picts_list)
i =0
forpictureinpicts_list:
(pixels, x_size, y_size) = load_picture(os.path.join(src_dir, picture))
#生成字符画图片
create_ascii_picture(pixels, symbols,os.path.join(dest_dir, picture), x_size, y_size)
print('正在生成中... {0}/{1}'.format(i, len_picts))
i +=1
def create_thumbnail(src_dir, dst_dir):
picts_list = sorted(os.listdir(src_dir))
forpictureinpicts_list:
base_name =os.path.basename(picture)
img = Image.open(os.path.join(src_dir, picture))
size =200,200
img.thumbnail(size, Image.ANTIALIAS)
img.save(os.path.join(dst_dir, base_name))
def load_picture(filename):
# Gray = R*0.299+ G*0.587+ B*0.114
img = Image.open(filename).convert('L')
(x, y) = img.size
pixels = list(img.getdata())
img.close()
return(pixels, x, y)
def create_ascii_picture(pixels, symbols, dest_name, x_size, y_size):
scale =4# 长宽扩大倍数
border =1# 边框宽度
interval_pixel =2#原图片间隔多少个像素点来填充
img = Image.new('L',
(x_size*scale +2*border,
y_size*scale +2*border),
255)
fnt = ImageFont.truetype('DejaVuSansMono.ttf', int(scale*3))
t = ImageDraw.Draw(img)
x = border
y = border
forjinrange(0, y_size, interval_pixel):
foriinrange(0, x_size, interval_pixel):
t.text( (x, y),
symbols[int(pixels[j*x_size + i]/256*len(symbols))],
font=fnt,
fill=0
)
x += scale * interval_pixel
x = border
y += scale * interval_pixel
img.save(dest_name,"JPEG")
def start_convert(src_file):
ifnotos.path.exists('temp_pic'):
os.mkdir('temp_pic')
ifnotos.path.exists('temp_thum'):
os.mkdir('temp_thum')
ifnotos.path.exists('temp_ascii'):
os.mkdir('temp_ascii')
#分离音频
slice_audio_cmd ='ffmpeg.exe -i {0} -vn temp.mp3'.format(src_file)
os.system(slice_audio_cmd)
#切割成图片
slice_pic_cmd ='ffmpeg.exe -i {0} -r 24 temp_pic/%06d.jpeg'.format(src_file)
os.system(slice_pic_cmd)
#生成缩略图
create_thumbnail('temp_pic','temp_thum')
#生成字符画
ascii_art_convert('temp_thum','temp_ascii')
#合成字符视频
dst_name =os.path.join(os.path.dirname(src_file),'ascii_'+os.path.basename(src_file))
merge_ascii_video_cmd ='ffmpeg -threads 2 -start_number 000001 -r 24 -i {0}/%06d.jpeg -i temp.mp3 -vcodec mpeg4 {1}'.format('temp_ascii', dst_name)
os.system(merge_ascii_video_cmd)
print('生成完成!')
ifos.path.exists('temp_pic'):
shutil.rmtree('temp_pic')
ifos.path.exists('temp_thum'):
shutil.rmtree('temp_thum')
ifos.path.exists('temp_ascii'):
shutil.rmtree('temp_ascii')
ifos.path.exists('temp.mp3'):
os.remove('temp.mp3')
if__name__ =='__main__':
src_file = sys.argv[1]
start_convert(src_file)
我们都知道Python容易学,但是就是不知道如何去学,去哪里找资料,在这里呢,python学习交流qq群233539995,分享我精心准备的Python学习资料,0基础到进阶!希望你们在学习Python道路上少走弯路!加油!
网友评论