用Python字符畫出了一個(gè)谷愛凌
之前經(jīng)常在網(wǎng)上看到那種由一個(gè)個(gè)字符構(gòu)成的視頻,非常炫酷。一直不懂是怎么做的,這兩天研究了一下,發(fā)現(xiàn)并不難。
先來(lái)看一個(gè)最終效果(如果模糊的話,點(diǎn)擊下方鏈接看高清版):
https://pan.baidu.com/s/1DvedXlDZ4dgHKLogdULogg 提取碼:1234
怎么實(shí)現(xiàn)的?
簡(jiǎn)單來(lái)說(shuō),要將一個(gè)彩色的視頻變成字符畫出來(lái)的黑白視頻,用下面幾步就能搞定:
- 對(duì)原視頻進(jìn)行抽幀,對(duì)每一幀黑白化,并將像素點(diǎn)用對(duì)應(yīng)的字符表示。
- 將表示出來(lái)的字符串再重新組合成字符圖像。
- 將所有的字符圖像再組合成字符視頻。
- 將原視頻的音頻導(dǎo)入到新的字符視頻中。
運(yùn)行方法
完整的代碼我放在文章末尾了,直接運(yùn)行python3 video2char.py即可。程序會(huì)要求你輸入視頻的本地路徑和轉(zhuǎn)變后的清晰度(0最模糊,1最清晰。當(dāng)然越清晰,轉(zhuǎn)變?cè)铰?/p>
運(yùn)行代碼的話需要用到tqdm、opencv_python、moviepy等幾個(gè)庫(kù),首先得pip3 install確保它們都有了。
原理分析
這里面最關(guān)鍵的步驟就是如何將一幀彩色圖像轉(zhuǎn)變?yōu)楹诎椎淖址麍D像,如下圖所示:

從青蛙公主視頻抽幀出來(lái)的

用字符畫出來(lái)的
而轉(zhuǎn)變的原理其實(shí)很簡(jiǎn)單。首先因?yàn)橐粋€(gè)字符畫在圖像里會(huì)占據(jù)很大一個(gè)像素塊,所以必須先對(duì)彩色圖像進(jìn)行壓縮,連續(xù)的一個(gè)像素塊可以合并,這個(gè)壓縮過程就是opencv的resize操作。
然后將壓縮后的像素點(diǎn)轉(zhuǎn)變?yōu)楹诎紫袼攸c(diǎn),并轉(zhuǎn)變?yōu)閷?duì)應(yīng)的字符。字符的話我這里采用的是下面的字符串,從黑到白,經(jīng)過我的實(shí)踐這一組是效果最好的:
"#8XOHLTI)i=+;:,. "
接著就需要將轉(zhuǎn)變后的字符畫到新的畫布上去,需要注意的點(diǎn)是排布得均勻緊湊了,畫布四周最好不要有太多多余的空白。
最后把所有的字符圖像合并成視頻就行了,但是合并后是沒有聲音的,需要用moviepy庫(kù)把原視頻的聲音導(dǎo)入過來(lái)。
完整代碼
import os
import re
import shutil
from tqdm import trange, tqdm
import cv2
from PIL import Image, ImageFont, ImageDraw
from moviepy.editor import VideoFileClip
class V2Char:
font_path = "Arial.ttf"
ascii_char = "#8XOHLTI)i=+;:,. "
def __init__(self, video_path, clarity):
self.video_path = video_path
self.clarity = clarity
def video2str(self):
def convert(img):
if img.shape[0] > self.text_size[1] or img.shape[1] > self.text_size[0]:
img = cv2.resize(img, self.text_size, interpolation=cv2.INTER_NEAREST)
ascii_frame = ""
for i in range(img.shape[0]):
for j in range(img.shape[1]):
ascii_frame += self.ascii_char[
int(img[i, j] / 256 * len(self.ascii_char))
]
return ascii_frame
print("正在將原視頻轉(zhuǎn)為字符...")
self.char_video = []
cap = cv2.VideoCapture(self.video_path)
self.fps = cap.get(cv2.CAP_PROP_FPS)
self.nframe = int(cap.get(cv2.CAP_PROP_FRAME_COUNT))
self.raw_width = int(cap.get(cv2.CAP_PROP_FRAME_WIDTH))
self.raw_height = int(cap.get(cv2.CAP_PROP_FRAME_HEIGHT))
font_size = int(25 - 20 * max(min(float(self.clarity), 1), 0))
self.font = ImageFont.truetype(self.font_path, font_size)
self.char_width, self.char_height = max(
[self.font.getsize(c) for c in self.ascii_char]
)
self.text_size = (
int(self.raw_width / self.char_width),
int(self.raw_height / self.char_height),
)
for _ in trange(self.nframe):
raw_frame = cv2.cvtColor(cap.read()[1], cv2.COLOR_BGR2GRAY)
frame = convert(raw_frame)
self.char_video.append(frame)
cap.release()
def str2fig(self):
print("正在生成字符圖像...")
col, row = self.text_size
catalog = self.video_path.split(".")[0]
if not os.path.exists(catalog):
os.makedirs(catalog)
blank_width = int((self.raw_width - self.text_size[0] * self.char_width) / 2)
blank_height = int((self.raw_height - self.text_size[1] * self.char_height) / 2)
for p_id in trange(len(self.char_video)):
strs = [self.char_video[p_id][i * col : (i + 1) * col] for i in range(row)]
im = Image.new("RGB", (self.raw_width, self.raw_height), (255, 255, 255))
dr = ImageDraw.Draw(im)
for i, str in enumerate(strs):
for j in range(len(str)):
dr.text(
(
blank_width + j * self.char_width,
blank_height + i * self.char_height,
),
str[j],
font=self.font,
fill="#000000",
)
im.save(catalog + r"/pic_{}.jpg".format(p_id))
def jpg2video(self):
print("正在將字符圖像合成字符視頻...")
catalog = self.video_path.split(".")[0]
images = os.listdir(catalog)
images.sort(key=lambda x: int(re.findall(r"\d+", x)[0]))
im = Image.open(catalog + "/" + images[0])
fourcc = cv2.VideoWriter_fourcc("m", "p", "4", "v")
savedname = catalog.split("/")[-1]
vw = cv2.VideoWriter(savedname + "_tmp.mp4", fourcc, self.fps, im.size)
for image in tqdm(images):
frame = cv2.imread(catalog + "/" + image)
vw.write(frame)
vw.release()
shutil.rmtree(catalog)
def merge_audio(self):
print("正在將音頻合成到字符視頻中...")
raw_video = VideoFileClip(self.video_path)
char_video = VideoFileClip(self.video_path.split(".")[0] + "_tmp.mp4")
audio = raw_video.audio
video = char_video.set_audio(audio)
video.write_videofile(
self.video_path.split(".")[0] + f"_{self.clarity}.mp4",
codec="libx264",
audio_codec="aac",
)
os.remove(self.video_path.split(".")[0] + "_tmp.mp4")
def gen_video(self):
self.video2str()
self.str2fig()
self.jpg2video()
self.merge_audio()
if __name__ == "__main__":
video_path = input("輸入視頻文件路徑:\n")
clarity = input("輸入清晰度(0~1, 直接回車使用默認(rèn)值0):\n") or 0
v2char = V2Char(video_path, clarity)
v2char.gen_video()
以上就是用Python字符畫出了一個(gè)谷愛凌的詳細(xì)內(nèi)容,更多關(guān)于Python字符畫的資料請(qǐng)關(guān)注腳本之家其它相關(guān)文章!
- Python將圖片轉(zhuǎn)換為字符畫的方法
- Python實(shí)現(xiàn)圖片轉(zhuǎn)字符畫的代碼實(shí)例
- Python簡(jiǎn)單實(shí)現(xiàn)圖片轉(zhuǎn)字符畫的實(shí)例項(xiàng)目
- Python制作動(dòng)態(tài)字符畫的源碼
- python繪制字符畫視頻的示例代碼
- Python實(shí)現(xiàn)視頻轉(zhuǎn)換為字符畫詳解
- 基于Python實(shí)現(xiàn)視頻轉(zhuǎn)字符畫動(dòng)漫小工具
- 利用Python字符畫生成甜心教主
- 如何利用python實(shí)現(xiàn)圖片轉(zhuǎn)化字符畫
相關(guān)文章
詳解如何從TensorFlow的mnist數(shù)據(jù)集導(dǎo)出手寫體數(shù)字圖片
這篇文章主要介紹了詳解如何從TensorFlow的mnist數(shù)據(jù)集導(dǎo)出手寫體數(shù)字圖片,文中通過示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來(lái)一起學(xué)習(xí)學(xué)習(xí)吧2019-08-08
Django中使用SMTP實(shí)現(xiàn)郵件發(fā)送功能
在?Django?中使用?SMTP?發(fā)送郵件是一個(gè)常見的需求,通常用于發(fā)送用戶注冊(cè)確認(rèn)郵件、密碼重置郵件等,下面我們來(lái)看看如何在?Django?中配置?SMTP?發(fā)送郵件吧2025-01-01
django API 中接口的互相調(diào)用實(shí)例
這篇文章主要介紹了django API 中接口的互相調(diào)用實(shí)例,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。一起跟隨小編過來(lái)看看吧2020-04-04
Python實(shí)現(xiàn)文字pdf轉(zhuǎn)換圖片pdf效果
當(dāng)我們把word轉(zhuǎn)化為pdf,wps默認(rèn)轉(zhuǎn)化為文字pdf,而圖片pdf要會(huì)員。所以本文將通過Python語(yǔ)言實(shí)現(xiàn)文字pdf轉(zhuǎn)換圖片pdf,需要的可以參考一下2022-04-04
Python 如何優(yōu)雅的將數(shù)字轉(zhuǎn)化為時(shí)間格式的方法
這篇文章主要介紹了Python 如何優(yōu)雅的將數(shù)字轉(zhuǎn)化為時(shí)間格式的方法,文中通過示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來(lái)一起學(xué)習(xí)學(xué)習(xí)吧2019-09-09
Django Auth用戶認(rèn)證組件實(shí)現(xiàn)代碼
這篇文章主要介紹了Django Auth用戶認(rèn)證組件實(shí)現(xiàn)代碼,文中通過示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友可以參考下2020-10-10

