1. 程式人生 > >Python將視訊轉成ASCII符號形式、生成GIF圖片!

Python將視訊轉成ASCII符號形式、生成GIF圖片!

Python將視訊轉成ASCII符號形式、生成GIF圖片!

 

一、簡要說明

  • 簡述:本文主要展示將視訊轉成ASCII符號形式展示出來,帶音訊。
  • 執行環境:Win10/Python3.5。
  • 主要模組: PIL、numpy、shutil。
  • [PIL]: 影象處理
  • [numpy]: 矩陣形式讀取圖片資料
  • *[shutil]: 刪除目錄
  • 注意點:ffmpeg.exe(視訊處理) 可以自行網上下載。

Python將視訊轉成ASCII符號形式、生成GIF圖片!

 

二、簡單分析

在網上看到轉成字元形式的視訊,感覺挺有趣的,於是查閱相關資料,開始實現一下。基本思路:主要使用 ffmpeg 對進行視訊操作,然後使用 PIL

 對圖片進行縮小、灰度和轉碼的處理。流程如下:

1. 建立臨時路徑。

2. 將視訊按幀分割成圖片存入臨時目錄。

3. 遍歷將圖片縮放、轉成灰度,再轉成ASCII形式的圖片。

4. 將ASCII形式的圖片合成視訊。

5. 獲取原始檔的音訊檔案。

6. 合併視訊和音訊檔案。

再來看看效果圖:

Python將視訊轉成ASCII符號形式、生成GIF圖片!

 

Python將視訊轉成ASCII符號形式、生成GIF圖片!

 

Python將視訊轉成ASCII符號形式、生成GIF圖片!

 

Python將視訊轉成ASCII符號形式、生成GIF圖片!

 

三、開發流程

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("[email protected]%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("[email protected]%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()