用Python字符畫出了一個(gè)谷愛凌
之前經(jīng)常在網(wǎng)上看到那種由一個(gè)個(gè)字符構(gòu)成的視頻,非常炫酷。一直不懂是怎么做的,這兩天研究了一下,發(fā)現(xiàn)并不難。
先來看一個(gè)最終效果(如果模糊的話,點(diǎn)擊下方鏈接看高清版):
https://pan.baidu.com/s/1DvedXlDZ4dgHKLogdULogg 提取碼:1234
怎么實(shí)現(xiàn)的?
簡(jiǎn)單來說,要將一個(gè)彩色的視頻變成字符畫出來的黑白視頻,用下面幾步就能搞定:
- 對(duì)原視頻進(jìn)行抽幀,對(duì)每一幀黑白化,并將像素點(diǎn)用對(duì)應(yīng)的字符表示。
- 將表示出來的字符串再重新組合成字符圖像。
- 將所有的字符圖像再組合成字符視頻。
- 將原視頻的音頻導(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è)庫,首先得pip3 install確保它們都有了。
原理分析
這里面最關(guān)鍵的步驟就是如何將一幀彩色圖像轉(zhuǎn)變?yōu)楹诎椎淖址麍D像,如下圖所示:
從青蛙公主視頻抽幀出來的
用字符畫出來的
而轉(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庫把原視頻的聲音導(dǎo)入過來。
完整代碼
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)文章
Python標(biāo)準(zhǔn)庫之Sys模塊使用詳解
這篇文章主要介紹了Python標(biāo)準(zhǔn)庫之Sys模塊使用詳解,本文講解了使用sys模塊獲得腳本的參數(shù)、處理模塊、使用sys模塊操作模塊搜索路徑、使用sys模塊查找內(nèi)建模塊、使用sys模塊查找已導(dǎo)入的模塊等使用案例,需要的朋友可以參考下2015-05-05對(duì)python 通過ssh訪問數(shù)據(jù)庫的實(shí)例詳解
今天小編就為大家分享一篇對(duì)python 通過ssh訪問數(shù)據(jù)庫的實(shí)例詳解,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。一起跟隨小編過來看看吧2019-02-02Python寫的Socks5協(xié)議代理服務(wù)器
這篇文章主要介紹了Python寫的Socks5協(xié)議代理服務(wù)器,代碼來自網(wǎng)上,需要的朋友可以參考下2014-08-08Python3實(shí)現(xiàn)帶附件的定時(shí)發(fā)送郵件功能
這篇文章主要為大家詳細(xì)介紹了Python3實(shí)現(xiàn)帶附件的定時(shí)發(fā)送郵件功能,文中示例代碼介紹的非常詳細(xì),具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下2018-02-02python使用Pillow將照片轉(zhuǎn)換為1寸報(bào)名照片的教程分享
在現(xiàn)代科技時(shí)代,我們經(jīng)常需要調(diào)整和處理照片以適應(yīng)特定的需求和用途,本文將介紹如何使用wxPython和Pillow庫,通過一個(gè)簡(jiǎn)單的圖形界面程序,將選擇的照片轉(zhuǎn)換為指定尺寸的JPG格式,并保存在桌面上,需要的朋友可以參考下2023-09-09python如何實(shí)現(xiàn)excel數(shù)據(jù)添加到mongodb
本文介紹了python是如何實(shí)現(xiàn)excel數(shù)據(jù)添加到mongodb,為了將數(shù)據(jù)導(dǎo)入mongodb,引入了pymongo,xlrd包,需要的朋友可以參考下2015-07-07