閱讀時長 | 用腦度 | 前置知識 |
---|---|---|
5min | 25% | Python |
最近看到一個 Up 主 Ele實驗室 發佈的一個視頻:字符化視頻是怎麼作出來的,感受頗有意思。不如本身也實現一個來玩玩?python
之前也沒怎麼寫過 Python,只用來刷過 LeetCode。正好借這個機會再學一學 Python 吧。git
先來看看實現效果。github
Emmm,有那味了。算法
首先,咱們都知道視頻本質上是一張張圖片快速展現的效果,因此第一步就是將視頻進行 分幀。shell
當將視頻分紅一張張圖片後,每張圖片裏的每一個像素點都是由 紅、綠、藍 三原色混合而成的。而這樣的混合機制就是經過數值值來表示的,好比 rgb(255, 255, 255) 就是白色,而 rgb(0, 0, 0) 則表示連個顏色都沒有,也就是黑色。數組
拿到三種值後,能夠經過必定計算將單像素變成一個值,通常來講這個過程能夠是灰度化。緩存
rgba(255, 255, 255) -> 0
複製代碼
拿到灰度後的值,就能夠將全部像素映射到 Hash 表上的一個字符,從而造成 字符畫。markdown
0 -> $
複製代碼
將這些字符畫都以 txt 文件保存到一個目錄,再按順序打印出來就造成了 字符視頻 了。ide
那咱們如今開始實現吧。svn
分幀這裏能夠不用咱們實現,直接使用 ffmpeg 就能夠了。先用下面命令進行安裝:
brew install ffmpeg
複製代碼
而後使用這個命令來分幀:
ffmpeg -i res/cxk-video.mov res/image_frames/%d.jpg
複製代碼
上面命令很容易理解:res/cxk-video.mov
是原視頻,後面的 res/image_frames/%d.jpg
就是存放的路徑,%d
表示數字.jpg。
這裏要借用到 Pillow 這個庫,能夠直接獲取圖片的 rgb 值。
先安裝一下這個庫:
pip3 install Pillow
複製代碼
若是你是 M1 的 Mac 電腦,須要用下面這兩個命令來安裝。
sudo python3 -m pip install --upgrade pip
sudo python3 -m pip install --upgrade Pillow
複製代碼
而後來實現將圖片變成字符畫:
from os import listdir
from os.path import isfile, join
image_frames_dir = 'res/image_frames'
txt_frames_dir = 'res/txt_frames'
def prepare(width, height):
for file_name in listdir(image_frames_dir):
print("正在處理 " + file_name)
image_path = join(image_frames_dir, file_name) // 獲取圖片地址
txt_path = join(txt_frames_dir, file_name.split('.')[0] + '.txt') // 獲取 txt 文件地址
if not isfile(image_path):
continue
image = Image.open(image_path) // 獲取圖片
image = image.resize((width, height), Image.NEAREST) # NEAREST 低質量圖
txt = to_string(image, width, height) // 生成字符文本
with open(txt_path, 'w') as txt_file: // 保存字符文本
txt_file.write(txt)
複製代碼
使用 getpiexel
獲取 tuple 而後經過算法生成 gray
值,再映射到定義好的數組上。
ascii_char = list("$@B%8&WM#*oahkbdpqwmZO0QLCJUYXzcvunxrjft/\|()1{}[]?-_+~<>i!lI;:,\"^`'. ")
def get_char(r, g, b, alpha=256):
if alpha == 0:
return ' '
length = len(ascii_char)
gray = int(0.2126 * r + 0.7152 * g + 0.0722 * b) // 生成灰度值
unit = (256.0 + 1) / length
return ascii_char[int(gray / unit)] // 映射到字符
def to_string(image, width, height):
txt = ""
for h in range(height):
for w in range(width):
txt += get_char(*image.getpixel((w, h))) # 獲取 pixel 的顏色數值
txt += '\n' # 記得最後要換行
return txt
複製代碼
而後就能夠生成不少個 txt 文件了。
好了,上面已經能夠實現將全部圖片轉換成字符畫了,下面將這些字符畫順序地打印出來就能夠了。
import os
from time import sleep
def display(speed):
def compare_file_name(file_name1, file_name2):
index1 = int(file_name1.split('.')[0])
index2 = int(file_name2.split('.')[0])
return index1 - index2 # 以文件名來做對比
# 獲取全部 txt 文件,並排好序
for file_name in sorted(listdir(txt_frames_dir), key=cmp_to_key(compare_file_name)):
txt_path = join(txt_frames_dir, file_name)
os.system('cat ' + txt_path) # cat 出來
sleep(speed)
複製代碼
如今已經能夠實現把坤坤打印出來了。
雖然功能已經實現好了,可是若是要作出一個別人也能玩得嗨的產品還須要再打磨一下。
首先,能夠添加 is_ready
函數來判斷是否已經有生成好了的字符畫。
from os import listdir
def is_ready():
return len(listdir(txt_frames_dir)) != 0
複製代碼
還能夠添加 clear 函數來清楚緩存。
import glob
import os
def clear():
files = glob.glob(join(txt_frames_dir, '*'))
for f in files:
os.remove(f)
複製代碼
最後一步,作一個入口文件,添加一些參數來自定義打出字符視頻:
#!/usr/local/bin/python3
import argparse
from procedure import prepare, display, is_ready, clear
# 獲取參數
def get_args():
parser = argparse.ArgumentParser()
parser.add_argument('command', type=str, default='run')
parser.add_argument('--width', type=int, default=240)
parser.add_argument('--height', type=int, default=100)
parser.add_argument('--speed', type=float, default=0.02)
return parser.parse_args()
if __name__ == '__main__':
args = get_args()
# 參數
command = args.command
width = args.width
height = args.height
speed = args.speed
if command == 'clear':
clear()
if command == 'compile':
prepare(width, height)
if command == 'run':
if not is_ready():
print('運行 python3 main.py compile 來編譯')
else:
display(speed) # 輸出字符視頻
複製代碼
用戶就能夠有多種玩法了:
./main.py run --speed 0.02 # 控制速度,單位爲 seconds,這裏數值爲默認值
./main.py run --width 240 --height 100 # 控制寬高,這裏數值爲默認值
複製代碼
上面全部完整的代碼均可以在 Github - cxk-dance 這個倉庫 裏獲取,各位觀衆老爺自行 clone 來玩吧。
在 M1 的 Mac 上有可能會出現 Pillow 安裝不成功的問題,在 README.md 也給出了相應的解決辦法。
對了,最近剛建了個公衆號【寫代碼的海怪】,以爲我寫得不錯就隨緣關注一下嘍~