点击上方↑↑↑蓝字关注我们~
「 Python开发者日」全日程揭晓,请扫码咨询 ↑↑↑
来源 |01二进制(ID:gh_d1999add1857)
编辑 | Jane
【导语】作者自称是一个经常逛B站的肥宅。最近B站上流行的视频素材除了“换脸”,其次就要属“蔡xx打球”视频了。有模仿的、对比的、手绘的......更过分的是,竟然有人在命令行输出了他的打球视频。不过,视频中的动画好像是用某个软件生成的txt文件,作者就在想既然都可以用txt输出了,能不能用python在命令行中显示呢?
说到这作者便开始搜索资料,做后制作了下面一段视频“
播放
代码是自己在网上查询资料后自己修改的,本着学习和分享的精神,今天就来分享下上面这段视频的制作过程。
原理
既然要开始做东西,首要的问题就是想好要怎么做,大家都知道视频是由一系列图片一帧一帧组成的,因此视频转字符动画最基本的便是图片转字符画。
在这里简单的说一下图片转字符画的原理:首先将图片转为灰度图,每个像素都只有亮度信息(用0~255表示)。然后我们构建一个有限字符集合,其中的每一个字符都与一段亮度范围对应,我们便可以根据此对应关系以及像素的亮度信息把每一个像素用对应的字符表示,这样字符画就形成了。Tips:如果对"灰度图像"这个概念不太理解的可以查阅百度百科
计算一张图片的灰度图像的方法如下(来自百度百科):
所以我们要做的就只是让字符画在命令行里面动起来就可以了。Tips:图片转字符画可以参考:/courses/370
准备
环境和工具:vscode、Mac OS、python 3.7
这次实验使用到的核心的库是opencv-python
Tips:这里分享一个我觉得还不错的opencv-python的中文文档:/aollo/aolloopencv/269602
实验
实验开始前我们需要安装opencv-python的包:
1pipinstallopencv
读取视频:
1defgenCharVideo(self,filepath):23self.charVideo=[]45#用opencv读取视频67cap=cv2.VideoCapture(filepath)89self.timeInterval=round(1/cap.get(5),3)1011nf=int(cap.get(7))1213print('Generatecharvideo,pleasewait...')1415foriinpyprind.prog_bar(range(nf)):1617#转换颜色空间,第二个参数是转换类型,cv2.COLOR_BGR2GRAY表示从BGR↔Gray1819rawFrame=cv2.cvtColor(cap.read()[1],cv2.COLOR_BGR2GRAY)frame=self.convert(rawFrame,os.get_terminal_size(),fill=True)2223self.charVideo.append(frame)2425cap.release()
这里的VideoCapture是用来读取视频的,cv2.cvtColor(input_imageflag)用于转换颜色空间,其中flag就是转换类型。对于BGR↔Gray的转换,我们使用的flag就是cv2.COLORBGR2GRAY。对于BGR↔HSV的转换我们用的flag就是cv2.COLORBGR2HSV。
将帧转换成字符画
1ascii_frame23ascii_char="$@B%8&WM#*oahkbdpqwmZO0QLCJUYXzcvunxrjft/|()1{}[]?-_+~<>i!lI;:,"^`'."4567#像素映射到字符89defpixelToChar(self,luminance):1011returnself.ascii_char[int(luminance/256*len(self.ascii_char))]12131415#将普通帧转为ASCII字符帧1617defconvert(self,img,limitSize=-1,fill=False,wrap=False):1819iflimitSize!=-1and(img.shape[0]>limitSize[1]orimg.shape[1]>limitSize[0]):img=cv2.resize(img,limitSize,interpolation=cv2.INTER_AREA)2223ascii_frame=''2425blank=''2627iffill:2829blank+=''*(limitSize[0]-img.shape[1])3031ifwrap:3233blank+='n'3435foriinrange(img.shape[0]):3637forjinrange(img.shape[1]):3839ascii_frame+=self.pixelToChar(img[i,j])4041ascii_frame+=blank4243returnascii_frame
这段代码其实就是将已经转变的灰度图的像素值映射到ascii_char上,然后输出到控制台。
控制输出
1#创建线程23getchar=threading.Thread(target=getChar)45#设置为守护线程67getchar.daemon=True89#启动守护线程1011getchar.start()1213#输出的字符画行数1415rows=len(self.charVideo[0])//os.get_terminal_size()[0]1617forframeinself.charVideo:1819#接收到输入则退出循环ifbreakflag:2223break2425self.streamOut(frame)2627self.streamFlush()2829time.sleep(self.timeInterval)3031#共rows行,光标上移rows-1行回到开始处3233self.streamOut('