用Python将抖音视频转换为字符视频

火山方舟人工智能与算法增长营销

picture.image

数据科学俱乐部

中国数据科学家社区

picture.image

介绍

字符视频就是画面全部由字符组成的,那就先来看看效果:

原视频如下:

那么用代码怎么实现的呢?下面用python实现,话不多说,直接上干货。

代码实现详解

其实总体思路分为3个步骤:

1.将原视频分割成若干个图片以及分离出音频

2.将每张图片转为字符画图片(重点部分)

3.将若干个字符画图片和音频合并成新的视频(字符视频)

  • 将原视频分割成若干个图片以及分离出音频

这个过程我们可以用python调用ffmpeg工具进行切割,ffmpeg是专门处理音视频的工具库。可以在ffmpeg官网下载可执行文件放在程序的当前目录

分离音频命令为:


        
ffmpeg.exe -i filename -vn temp.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))  
  
    for picture in picts_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库直接转灰度:


        
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)  

    

2.将灰度图的每个像素点替换为相应的字符

这里如何替换呢?可以根据灰阶值来替换为我们自己设定的字符,例如:


        
symbols = list("$@B%8&WM#*oahkbdpqwmZO0QLCJUYXzcvunxrjft/\|()1{}[]?-\_+~<>i!lI;:,\"^`'. ")  

    

从上面列表可以看到:越靠前的越密集,越往后越稀疏,于是我们根据灰阶值的大小按比例取列表中的字符,灰阶值越大,取越靠后的字符,这样图片轮廓才能更好的清晰显示

为了是转化后的字符图片看起来不密集以及提高转化时间,我这里将每间隔1个像素来替换字符,初始还要指定图片的边框及尺寸,这些参数可以自行调整,具体展示如下代码:


        
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  
    for j in range(0, y\_size, interval\_pixel):  
        for i in range(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  
import os, 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  
  
    for picture in picts_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))  
  
    for picture in picts_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  
    for j in range(0, y_size, interval_pixel):  
        for i in range(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):  
  
    if not os.path.exists('temp\_pic'):  
        os.mkdir('temp\_pic')  
  
    if not os.path.exists('temp\_thum'):  
        os.mkdir('temp\_thum')  
  
    if not os.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('生成完成!')  
  
  
    if os.path.exists('temp\_pic'):  
        shutil.rmtree('temp\_pic')  
  
    if os.path.exists('temp\_thum'):  
        shutil.rmtree('temp\_thum')  
  
    if os.path.exists('temp\_ascii'):  
        shutil.rmtree('temp\_ascii')  
  
    if os.path.exists('temp.mp3'):  
        os.remove('temp.mp3')  
  
  
if __name__ == '\_\_main\_\_':  
  
    src_file = sys.argv[1]  
    start_convert(src_file)
    

点击这里参与Python编程学习

本文作者

zarten,互联网一线工作者。

地址:zhihu.com/people/zarten

投稿邮箱:pythonpost@163.com

欢迎点击申请 Python中文社区新专栏作者计划

picture.image

Python中文社区作为一个去中心化的全球技术社区,以成为全球20万Python中文开发者的精神部落为愿景,目前覆盖各大主流媒体和协作平台,与阿里、腾讯、百度、微软、亚马逊、开源中国、CSDN等业界知名公司和技术社区建立了广泛的联系,拥有来自十多个国家和地区数万名登记会员,会员来自以公安部、工信部、清华大学、北京大学、北京邮电大学、中国人民银行、中科院、中金、华为、BAT、谷歌、微软等为代表的政府机关、科研单位、金融机构以及海内外知名公司,全平台近20万开发者关注。

picture.image

点击下方 阅读原文 免费成为 社区会员

0
0
0
0
评论
未登录
看完啦,登录分享一下感受吧~
暂无评论