利用Python让你的命令行像坤坤一样会打篮球

640?wx_fmt=jpeg

该图片由F. Muhammad在Pixabay上发布

完整代码可在本公众号或者公众号「01二进制」后台回复:「篮球」获取

前言

承接上文,作为一个经常逛b站的肥宅,近期b站上除了流行"品如”素材的视频,更多的莫过于蔡xx打球视频的了,有模仿的,有对比的,有手绘的,更过分的是竟然有人在命令行输出了他的打球视频,不过视频中的动画好像是用某个软件生成的txt文件,看到这我就在想既然都可以用txt输出了,为啥不能用python在命令行中输出呢?说到这我便开始搜索资料,然后制作了下面一段视频:

代码是我在网上查询资料后自己修改的,本着学习和分享的精神,今天我来分享下上面这段视频的制作过程。

原理

既然要开始做东西,首要的问题就是想好要怎么做,大家都知道视频是由一系列图片一帧一帧组成的,因此视频转字符动画最基本的便是图片转字符画。

在这里简单的说一下图片转字符画的原理:首先将图片转为灰度图,每个像素都只有亮度信息(用 0~255 表示)。然后我们构建一个有限字符集合,其中的每一个字符都与一段亮度范围对应,我们便可以根据此对应关系以及像素的亮度信息把每一个像素用对应的字符表示,这样字符画就形成了。

Tips:如果对"灰度图像"这个概念不太理解的可以查阅百度百科

计算一张图片的灰度图像的方法如下(来自百度百科):

640?wx_fmt=jpeg

所以我们要做的就只是让字符画在命令行里面动起来就可以了。

Tips:图片转字符画可以参考:https://www.shiyanlou.com/courses/370

准备

环境和工具:

  • vscode

  • Mac OS

  • python 3.7

这次实验使用到的核心的库是opencv-python,关于opencv上篇文章已经简单介绍过了,这里不多阐述了,只要知道这是一个和计算机视觉有关的库就可以了。

Tips:这里分享一个我觉得还不错的opencv-python的中文文档:https://www.kancloud.cn/aollo/aolloopencv/269602

实验

实验开始前我们需要安装opencv-python的包:

pip install opencv-python

读取视频

def genCharVideo(self, filepath):	
    self.charVideo = []	
    # 用opencv读取视频	
    cap = cv2.VideoCapture(filepath)	
    self.timeInterval = round(1 / cap.get(5), 3)	
    nf = int(cap.get(7))	
    print('Generate char video, please wait...')	
    for i in pyprind.prog_bar(range(nf)):	
        # 转换颜色空间,第二个参数是转换类型,cv2.COLOR_BGR2GRAY表示从BGR↔Gray	
        rawFrame = cv2.cvtColor(cap.read()[1], cv2.COLOR_BGR2GRAY)	
        frame = self.convert(rawFrame, os.get_terminal_size(), fill=True)	
        self.charVideo.append(frame)	
    cap.release()

这里的 VideoCapture是用来读取视频的, cv2.cvtColor(input_imageag)用于转换颜色空间,其中flag就是转换类型。对于BGR↔Gray的转换,我们使用的flag就是cv2.COLORBGR2GRAY。对于BGR↔HSV的转换我们用的flag就是cv2.COLORBGR2HSV。

将帧转换成字符画

ascii_char = "$@B%8&WM#*oahkbdpqwmZO0QLCJUYXzcvunxrjft/\|()1{}[]?-_+~<>i!lI;:,\"^`'. "	
# 像素映射到字符	
def pixelToChar(self, luminance):	
    return self.ascii_char[int(luminance / 256 * len(self.ascii_char))]	
# 将普通帧转为 ASCII 字符帧	
def convert(self, img, limitSize=-1, fill=False, wrap=False):	
    if limitSize != -1 and (img.shape[0] > limitSize[1] or img.shape[1] > limitSize[0]):	
        img = cv2.resize(img, limitSize, interpolation=cv2.INTER_AREA)	
    ascii_frame = ''	
    blank = ''	
    if fill:	
        blank += ' ' * (limitSize[0] - img.shape[1])	
    if wrap:	
        blank += '\n'	
    for i in range(img.shape[0]):	
        for j in range(img.shape[1]):	
            ascii_frame += self.pixelToChar(img[i, j])	
        ascii_frame += blank	
    return ascii_frame

这段代码其实就是将已经转变的灰度图的像素值映射到 ascii_char上,然后输出到控制台。

控制输出

# 创建线程	
getchar = threading.Thread(target=getChar)	
# 设置为守护线程	
getchar.daemon = True	
# 启动守护线程	
getchar.start()	
# 输出的字符画行数	
rows = len(self.charVideo[0]) // os.get_terminal_size()[0]	
for frame in self.charVideo:	
    # 接收到输入则退出循环	
    if breakflag:	
        break	
    self.streamOut(frame)	
    self.streamFlush()	
    time.sleep(self.timeInterval)	
    # 共 rows 行,光标上移 rows-1 行回到开始处	
    self.streamOut('\033[{}A\r'.format(rows - 1))	
# 光标下移 rows-1 行到最后一行,清空最后一行	
self.streamOut('\033[{}B\033[K'.format(rows - 1))	
# 清空最后一帧的所有行(从倒数第二行起)	
for i in range(rows - 1):	
    # 光标上移一行	
    self.streamOut('\033[1A')	
    # 清空光标所在行	
    self.streamOut('\r\033[K')	
if breakflag:	
    self.streamOut('User interrupt!\n')	
else:	
    self.streamOut('Finished!\n')

执行

最后在main函数中设置下要读取的文件名,再play一下就可以了

if __name__ == '__main__':	
    v2char = V2Char('vedio.mp4')	
    v2char.play()

完整代码可在本公众号或者公众号「01二进制」后台回复:「篮球」获取

写在最后

方法教给大家了,视频素材可以优化,大家可以自己收集好的视频素材,发到朋友圈,让代码骚动起来!

日常推荐

用Python爬一爬吴亦凡大碗宽面,情感分析自黑式圈粉!

年薪80+万的大数据开发全套教程,都在这儿!

今日问题

今天思考一下:谈谈HashMap和Hashtable之间的区别和联系

打卡格式:打卡第n天,答:xxx

640?wx_fmt=png

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值