运行环境:Win10/Python3.5。
主要模块: PIL、numpy、shutil。
[PIL]: 图像处理
[numpy]: 矩阵形式读取图片数据
*[shutil]: 删除目录
注意点:ffmpeg.exe(视频处理) 可以自行网上下载。
本文主要参考:Python将视频转换为全字符视频(含音频)
二、简单分析在网上看到转成字符形式的视频,感觉挺有趣的,于是查阅相关资料,开始实现一下。基本思路:主要使用 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这一步是重点,在遍历获取源图片目录列表之后,就可以分步进行操作了:
缩小图片、转成灰度模式,存入数组。
新建空白图片(灰度模式、与原始图片等宽高)。
将每个字符绘制在一定的区域内。
保存字符图片。
下面就是替换的字符:
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)