最早看到字符动画大概是在AB站的弹幕中看到,最近闲来无事就在网上查找了相关的资料,了解下是如何做到的,因为在学python,所以留下此文以便加强记忆。
大概整理下转换的步骤:
1.使用OpenCV读取视频的每帧,并将每帧都转为灰度图
2.将每帧的灰度图转为字符串
3.将每帧的字符串在命令行打印出来
因为本次对视频的处理提取用到了python的第三方包OpenCV,所以第一部自然是安装它,通过pip包管理器非常简单:
pip install opencv-python
下面就可以开始了,新建一个practise.py的文件,
首先需要导入OpenCV的包:
import cv2
前面已经讲过大致问分为3布,所以对应的方法也是有三个,从第一步开始:
def get_img(video_src,size,number): num=0 img_list=[] cap=cv2.VideoCapture(video_src) while cap.isOpened(): ret,frame =cap.read() if ret: gray = cv2.cvtColor(frame,cv2.COLOR_BGR2GRAY) img=cv2.resize(gray,size,interpolation=cv2.INTER_AREA) img_list.append(img) num+=1 if num==number: return img_list else: break return img_list
这个方法有三个参数:第一个很明显是视频源,第二个是指定视频的长和宽,第三个是需要转换的帧数。
通过cv2.VideoCapture()这个OpenCV提供的读取方法将视频读入内存,然后开始循环;接着通过cv2.cvtColor(frame,cv2.COLOR_BGR2GRAY)转为灰度图,再接着用cv2.resize(gray,size,interpolation=cv2.INTER_AREA)转成对应的大小。循环过后将帧的list返出。
第二步:
分成两小步,第一是将每一帧转字符串
def convert(img): res=[] height,width=img.shape for row in range(height): line="" for col in range(width): percent=img[row][col]/255 index=int(percent*(len(pixels)-1)) line+=pixels[index]+" " res.append(line) return res
通过img.shape获取到视频的长和宽,然后根据每个像素点的灰度将它转换为对应的符号,这里定义一个字符串根据灰度的加深而选择不同的字符:
pixels = " .,-'`:!1+*abcdefghijklmnopqrstuvwxyz<>()\/{}[]?234567890ABCDEFGHIJKLMNOPQRSTUVWXYZ%&@#$"
可以看出来字符越往后相对的占用像素点也越多。可以把像素点看作一个二位数组来遍历,因为灰度最高是255,所以percent=img[row][col]/255将其转换成百分比,接着诚意字符串的长度获取index就知道使用哪个字符来表示这个像素点了。遍历完成后将结果拼成一个字符串返出。
需要注意的是line+=pixels[index]+” “,因为命令行中,横向的字符之间几乎没有间隙,而纵向会存在一个行间距,所以手动增加一个空格来避免视频被压缩。
第二是循环转换视频的每一帧:
def video_convert(imgs): video_chars=[] for img in imgs: chars=convert(img) video_chars.append(chars) return video_chars
第三步:
def play_video(video_chars): lines = len(video_chars[0]) for pic_i in range(len(video_chars)): for line_i in range(lines): print(video_chars[pic_i][line_i]) os.system("cls")
将第二步生成的字符串作为参数传入这个方法。
首先取字符串的第一个作为视频的行数,接着就是遍历每帧的每行进行打印。每打印完一帧需要通过系统函数命令行cls进行清屏以继续打印下一帧。
这样基本流程就实现了,然后就可以通过一个主函数执行下即可:
if __name__ =="__main__": t1=cv2.getTickCount() imgs=get_img("test.mp4",(100,60),200) video_chars=video_convert(imgs) t2=cv2.getTickCount() print("用时:%s s"%((t2-t1)/cv2.getTickFrequency())) input("转换完成,按回车播放") play_video(video_chars)
到这里就给大家介绍完了,这段代码现在其实很存在不少问题,首先就是效率很低下,比如例子中的100*60的200帧转换大概需要12秒,这个问题我们可以考虑使用多线程来解决。
另外还有就是不能重复播放,每次放完就需要重新转换。还有就是考虑如何转换成彩色的视频等等,都是可以深入的地方。
版权所属,如需转载,请注明出处:搜闲鱼