"視頻轉字元動畫 Python 60行代碼" , 轉載請註明出處。 昨晚一朋友跟我說在網上看到了別人做的字元視頻,覺得很厲害,我於是也打算玩玩。今天中午花時間實現了這樣一個小玩意。 順便把過程記錄在這裡。 步驟 1. 把視頻的幀保存為圖片 2. 把圖片轉化為字元圖片 3. 按順序播放圖片 模塊 這個 ...
視頻轉字元動畫-Python-60行代碼, 轉載請註明出處。
昨晚一朋友跟我說在網上看到了別人做的字元視頻,覺得很厲害,我於是也打算玩玩。今天中午花時間實現了這樣一個小玩意。
順便把過程記錄在這裡。
步驟
- 把視頻的幀保存為圖片
- 把圖片轉化為字元圖片
- 按順序播放圖片
模塊
這個程式需要用到這樣幾個模塊:
- opencv #用來讀取視頻,讀取有些壓縮視頻可能還要安裝解碼器
- Pillow #圖像處理,本來opencv大概也可以做,但是我不太熟悉它,所以還是用pillow
- time.sleep() #用來調節播放速度
讀取視頻
首先需要安裝opencv模塊
pip install opencv-python
然後用這個程式把圖片提取出來
import numpy as np
import cv2
cap = cv2.VideoCapture('BadApple.mp4')
num = 0
while (cap.isOpened()):
print('pic',num)
ret, frame = cap.read()
gray = cv2.cvtColor(frame, cv2.COLOR_BGR2GRAY)
cv2.imwrite("img%d.png" % num, frame)
num += 1
print('finish')
逐行解釋:
2-3行:導入numpy和opencv模塊(雖然不知道為什麼要numpy,但是opencv官方文檔就這麼寫的,我也就抄了下來)
5行: 讀取視頻,BadApple.mp4(點擊下載)換成你需要轉換的視頻(有些格式的視頻可能需要安裝解碼器才能正常讀取)
8行: 沒有讀到視頻尾部前,cap.isOpend()會一直返回True
9行: 告訴你正在處理第幾張圖片
10行: cap.read() 逐幀讀取視頻,返回一個img對象。
11行: 顏色空間轉換,把彩圖變成灰度圖,你也可以去丟掉它,在轉字元時用彩色字元。
12行: 保存圖片
運行結束會提示"Done!",不出問題的話這時程式目錄內會出現幾千張圖片
圖片轉化為字元畫
首先安裝pillow圖形庫
pip install pillow
然後運行這個腳本把所有圖片轉化為字元畫
from PIL import Image
size = (64, 48)
def resize_pic(img):
im = Image.open(img, 'r').convert("L")
im = im.resize(size)
return im
def pic_to_char(num):
video = open("video.txt", "w")
for i in range(num):
im = resize_pic('img%d.png' % i)
for y in range(size[1]):
for x in range(size[0]):
color = ' ' if im.getpixel((x, y))>210 else '* '
video.write(color)
video.write("\n")
print(i)
video.close()
print('Done!')
逐行解釋:
1-3行: 導入pillow庫,size是轉化出的字元圖片的尺寸,註意一是控制在40-80內保證你的屏幕能完整地顯示圖片,二是比例要和原視頻一致。
5-9行: 得到一個圖片文件名,Image.open()讀取圖片,convert("L")把它轉換為灰度圖,由於之前讀取圖片時已經轉化了,你可以去掉它,寫在這裡只是說明還有第二種方法。
im.resize()把im尺寸轉為size,返迴轉化後對象
10-: pic_to_char()接受一個num參數,表示一共有多少張圖片,把轉換出的字元全部寫入一個'video.txt'文件。
im.getpixel()接受一個二元組(x,y),返回坐標為x,y的像素的顏色,因為這裡是灰度圖,所以返回值是一個整數。可以聯想到如果是RGB圖,會返回一個三元組。
210是灰度值,大於它的像素會被轉換為"* ",多一個空格的原因是字元界面行之間有間距,空格用於抵消這個行距,保持長寬比。
因為實例視頻對比非常鮮明,所以就只用兩種字元代表黑和白了。如果你需要更強表現力,可以多用幾個字元,甚至添加彩色字元。你可以自行修改這個部分。
播放字元視頻
我們把所有的圖片不作區分地寫入了video.txt,可是怎樣用這個文件顯示我們的字元畫呢?
前面已經知道我們的每張圖高度為48(在size里定義),那麼每次從文本中讀取48行顯示在字元界面上,然後游標移回到第一行行首,再次讀取48行顯示,新的字元會覆蓋原有的字元串,這樣的效果就是字元視頻了。
代碼如下
def gotoxy():
pass
video = open(r"video.txt", "r")
num = 1
for line in video:
print(line,end='')
if num % size[1] == 0:
sleep(0.005)
gotoxy()
num += 1
逐行解析:
1-3行:gotoxy()函數將游標移到行首,其實現後面再說
4- : 打開video.txt(這裡一定要輸入完整的路徑,不要像我這麼懶),迭代讀取並輸出每一行(由於文本內已經寫入了換行符,所以指定print以''空字元結尾),每輸出48行休眠0.005s,sleep()用於調節播放速度。休眠之後將游標移到行首,如此往複。
現在還有一個gotoxy()沒實現,本來打我算用curses模塊來做的,這樣會簡單不少。但是看了半小時文檔還是不太會用(勿噴),所以直接做了個c語言拓展,用c語言的windows.h 的函數實現了cmd字元界面的定位。但是這樣程式就變複雜了,畢竟還要自己寫c語言包裝。如果你會curses模塊,願意指點一二,請告訴我怎麼做。
先說好寫拓展比較複雜,所以我把它放在最後了。怕麻煩的可以直接下載我編譯好的文件(win64系統,其他平臺需要自己編譯),或者去研究一下curses,或者直接用c語言實現這個play()函數。
http://odxw2uear.bkt.clouddn.com/gotoxy.cp35-win_amd64.pyd
http://odxw2uear.bkt.clouddn.com/gotoxy-0.0.0-py3.5.egg-info
下載這兩個文件放到你的python/Lib/site-packages文件夾內,把上面的gotoxy()改為
import gotoxy.flush as flush()
def gotoxy():
flush()
這樣,你解決了gotoxy()這個麻煩,在命令行界面運行python play.py
你就可以得到這樣的成果。
效果請點這裡
總共不到六十行代碼。(c拓展不算)
p.s.其實完全不需要把圖片保存下來再轉字元串,這裡把代碼分成三個部分只是便於理解,你甚至都不需要把字元保存到video.txt文件中。把這三個部分整合到一起的過程就交給你了,少年!
c拓展
準備好接受包裝的衝擊了嗎?我們開始吧!
我的c源碼如下
#include <conio.h>
#include <stdio.h>
#include <windows.h>
int flush() //這個就是我們需要的函數,因為不知道取什麼名字好,隨便寫了個。
{
//隱藏游標
CONSOLE_CURSOR_INFO cursor_info = {1, 0};
SetConsoleCursorInfo(GetStdHandle(STD_OUTPUT_HANDLE), &cursor_info);
//游標回到行首
COORD pos = {0,0};
HANDLE h0ut = GetStdHandle(STD_OUTPUT_HANDLE);
SetConsoleCursorPosition (h0ut, pos);
return 0;
}
需要用到的就是這個函數,下麵是函數包裝部分,我也前天才學了一點點c包裝方法,寫得不好就別噴了~~
#include "Python.h"
//封裝
static PyObject*
gotoxy_flush(PyObject *self, PyObject *args)
{
return (PyObject*)Py_BuildValue("i", flush()); //雖然說完全不需要返回值,還是返回一個吧
};
static PyMethodDef
ExtestMethods[] = { //方法數組
{"flush",gotoxy_flush,METH_VARARGS},
{NULL}, //NULL結尾
};
static struct PyModuleDef Extestmodule = {
PyModuleDef_HEAD_INIT,
"gotoxy", //name of moudle
"a test module", //Docs string(may be null)
-1, //size of pre-interpreter state or -1
ExtestMethods, //method table
};
//module initialization function
PyMODINIT_FUNC
PyInit_gotoxy(void){
return PyModule_Create(&Extestmodule);
}
然後寫一個setup.py來編譯並安裝這個模塊,我就不解釋了,沒學過的可以先看看《python核心編程(3 rd)》瞭解一下包裝方法。