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


    一、简要说明
    • 简述:本文主要展示将视频转成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 + '
    ')
    
            # 源视频文件的图片路径
            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 + '
    ')
            # 使用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 + '
    ')
            # 读取原始图片目录
            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 + '
    ')
            # 输出视频保存路径
            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 + '
    ')
    
            # 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 + '
    ')
    
            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 + '
    ')
    
            # 源视频文件的图片路径
            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 + '
    ')
            # 使用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 + '
    ')
            # 读取原始图片目录
            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 + '
    ')
            # 输出视频保存路径
            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 + '
    ')
    
            # 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 + '
    ')
    
            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()
    
  • 相关阅读:
    解决CHM文件不能浏览的问题
    SAS宏功能(下)
    python一句话求素数
    SAS与DBMS系统(SqlServer)通信
    CSS3圆角
    水平居中和垂直居中
    滚动条的控制
    快来给你的桌面加一只可爱的蟑螂吧 那个人
    自己用的PHP缓存类
    jquery 全反选
  • 原文地址:https://www.cnblogs.com/reader/p/10172919.html
Copyright © 2020-2023  润新知