使用python将视频转换为字符视频

最早看到字符动画大概是在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秒,这个问题我们可以考虑使用多线程来解决。
另外还有就是不能重复播放,每次放完就需要重新转换。还有就是考虑如何转换成彩色的视频等等,都是可以深入的地方。

版权所属,如需转载,请注明出处:搜闲鱼

2,614 次浏览

发表评论

邮箱地址不会被公开。 必填项已用*标注