效果展示
代碼示例
import sys
import os
import time
import threading
import cv2
import pyprind
class CharFrame:
ascii_char = "$@B%8&WM#*oahkbdpqwmZO0QLCJUYXzcvunxrjft/\|()1{}[]?-_+~<>i!lI;:,\"^`'. "
# 像素映射到字符
def pixelToChar(self, luminance):
return self.ascii_char[int(luminance / 256 * len(self.ascii_char))]
# 將普通幀轉為 ASCII 字符幀
def convert(self, img, limitSize=-1, fill=False, wrap=False):
if limitSize != -1 and (img.shape[0] > limitSize[1] or img.shape[1] > limitSize[0]):
img = cv2.resize(img, limitSize, interpolation=cv2.INTER_AREA)
ascii_frame = ''
blank = ''
if fill:
blank += ' ' * (limitSize[0] - img.shape[1])
if wrap:
blank += '\n'
for i in range(img.shape[0]):
for j in range(img.shape[1]):
ascii_frame += self.pixelToChar(img[i, j])
ascii_frame += blank
return ascii_frame
class V2Char(CharFrame):
charVideo = []
timeInterval = 0.033
def __init__(self, path):
if path.endswith('txt'):
self.load(path)
else:
self.genCharVideo(path)
def genCharVideo(self, filepath):
self.charVideo = []
# 用opencv讀取視頻
cap = cv2.VideoCapture(filepath)
self.timeInterval = round(1 / cap.get(5), 3)
nf = int(cap.get(7))
print('Generate char video, please wait...')
for i in pyprind.prog_bar(range(nf)):
# 轉換顏色空間,第二個參數是轉換類型,cv2.COLOR_BGR2GRAY表示從BGR↔Gray
rawFrame = cv2.cvtColor(cap.read()[1], cv2.COLOR_BGR2GRAY)
frame = self.convert(rawFrame, os.get_terminal_size(), fill=True)
self.charVideo.append(frame)
cap.release()
def export(self, filepath):
if not self.charVideo:
return
with open(filepath, 'w') as f:
for frame in self.charVideo:
# 加一個換行符用以分隔每一幀
f.write(frame + '\n')
def load(self, filepath):
self.charVideo = []
# 一行即為一幀
for i in open(filepath):
self.charVideo.append(i[:-1])
def play(self, stream=1):
# Bug:
# 光標定位轉義編碼不兼容 Windows
if not self.charVideo:
return
if stream == 1 and os.isatty(sys.stdout.fileno()):
self.streamOut = sys.stdout.write
self.streamFlush = sys.stdout.flush
elif stream == 2 and os.isatty(sys.stderr.fileno()):
self.streamOut = sys.stderr.write
self.streamFlush = sys.stderr.flush
elif hasattr(stream, 'write'):
self.streamOut = stream.write
self.streamFlush = stream.flush
breakflag = False
def getChar():
nonlocal breakflag
try:
# 若系統為 windows 則直接調用 msvcrt.getch()
import msvcrt
except ImportError:
import termios
import tty
# 獲得標准輸入的文件描述符
fd = sys.stdin.fileno()
# 保存標准輸入的屬性
old_settings = termios.tcgetattr(fd)
try:
# 設置標准輸入為原始模式
tty.setraw(sys.stdin.fileno())
# 讀取一個字符
ch = sys.stdin.read(1)
finally:
# 恢復標准輸入為原來的屬性
termios.tcsetattr(fd, termios.TCSADRAIN, old_settings)
if ch:
breakflag = True
else:
if msvcrt.getch():
breakflag = True
# 創建線程
getchar = threading.Thread(target=getChar)
# 設置為守護線程
getchar.daemon = True
# 啟動守護線程
getchar.start()
# 輸出的字符畫行數
rows = len(self.charVideo[0]) // os.get_terminal_size()[0]
for frame in self.charVideo:
# 接收到輸入則退出循環
if breakflag:
break
self.streamOut(frame)
self.streamFlush()
time.sleep(self.timeInterval)
# 共 rows 行,光標上移 rows-1 行回到開始處
self.streamOut('\033[{}A\r'.format(rows - 1))
# 光標下移 rows-1 行到最后一行,清空最后一行
self.streamOut('\033[{}B\033[K'.format(rows - 1))
# 清空最后一幀的所有行(從倒數第二行起)
for i in range(rows - 1):
# 光標上移一行
self.streamOut('\033[1A')
# 清空光標所在行
self.streamOut('\r\033[K')
if breakflag:
self.streamOut('User interrupt!\n')
else:
self.streamOut('Finished!\n')
if __name__ == "__main__":
v2char = V2Char('./vedio.mp4')
v2char.play()
歡迎斧正,that's all see also:[]()