python3_python3.7 - CSDN
精华内容
参与话题
  • Python3开发详解

    万人学习 2019-12-18 15:09:51
    Python3 开发详解,课程从基础的环境搭建讲起,详细讲述了Python开发的方方面面,内容包括:编程基础、函数、数据结构、异常处理、字符串、数字、网络编程、多线程、数据库处理等。
  • 玩转Python-Python3基础入门

    千人学习 2020-07-21 20:23:03
    总课时80+,提供源码和相关资料
  • 随着深度学习库tensorflow等对python3的完全支持,python3正在替代机器学习和大数据的python2地位,事实也已经如此,大部分机器学习和大数据项目正在使用python3。本课程通过对python3.x深入浅出的讲解,以实战上机...
  • import sys import os import time import threading import cv2 import pyprind # 图片转字符画的原理:首先将图片转为灰度图,每个像素都只有亮度信息(用 0~255 表示)。 # 然后我们构建一个有限字符集合,其中...
    import sys
    import os
    import time
    import threading
    import cv2
    import pyprind
    
    # 图片转字符画的原理:首先将图片转为灰度图,每个像素都只有亮度信息(用 0~255 表示)。
    # 然后我们构建一个有限字符集合,其中的每一个字符都与一段亮度范围对应,
    # 我们便可以根据此对应关系以及像素的亮度信息把每一个像素用对应的字符表示,这样字符画就形成了。
    
    
    # luminance 亮度
    class CharFrame:
    
        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
    
    
    class I2Char(CharFrame):
    
        result = None
    
        def __init__(self, path, limitSize=-1, fill=False, wrap=False):
            self.genCharImage(path, limitSize, fill, wrap)
    
        def genCharImage(self, path, limitSize=-1, fill=False, wrap=False):
            img = cv2.imread(path, cv2.IMREAD_GRAYSCALE)
            if img is None:
                return
            self.result = self.convert(img, limitSize, fill, wrap)
    
        def show(self, stream = 2):
            if self.result is None:
                return
            if stream == 1 and os.isatty(sys.stdout.fileno()):
                self.streamOut = sys.stdout.write
                self.streamFlush = sys.stdout.flush
            elif stream == 2 and os.isatty(sys.stderr.fileno()):
                self.streamOut = sys.stderr.write
                self.streamFlush = sys.stderr.flush
            elif hasattr(stream, 'write'):
                self.streamOut = stream.write
                self.streamFlush = stream.flush
            self.streamOut(self.result)
            self.streamFlush()
            self.streamOut('\n')
    
    
    class V2Char(CharFrame):
    
        charVideo = []
        timeInterval = 0.033
    
        def __init__(self, path):
            if path.endswith('txt'):
                self.load(path)
            else:
                self.genCharVideo(path)
    
        def genCharVideo(self, filepath):
            self.charVideo = []
            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)):
                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()
    
        def export(self, filepath):
            if not self.charVideo:
                return
            with open(filepath,'w') as f:
                for frame in self.charVideo:
                    # 加一个换行符用以分隔每一帧
                    f.write(frame + '\n')
    
        def load(self, filepath):
            self.charVideo = []
            # 一行即为一帧
            for i in  open(filepath):
                self.charVideo.append(i[:-1])
    
        def play(self, stream = 1):
            # Bug:
            # 光标定位转义编码不兼容 Windows
            if not self.charVideo:
                return
            if stream == 1 and os.isatty(sys.stdout.fileno()):
                self.streamOut = sys.stdout.write
                self.streamFlush = sys.stdout.flush
            elif stream == 2 and os.isatty(sys.stderr.fileno()):
                self.streamOut = sys.stderr.write
                self.streamFlush = sys.stderr.flush
            elif hasattr(stream, 'write'):
                self.streamOut = stream.write
                self.streamFlush = stream.flush
            breakflag = False
    
            def getChar():
                nonlocal breakflag
                try:
                    # 若系统为 windows 则直接调用 msvcrt.getch()
                    import msvcrt
                except ImportError:
                    import termios, tty
                    # 获得标准输入的文件描述符
                    fd = sys.stdin.fileno()
                    # 保存标准输入的属性
                    old_settings = termios.tcgetattr(fd)
                    try:
                        # 设置标准输入为原始模式
                        tty.setraw(sys.stdin.fileno())
                        # 读取一个字符
                        ch = sys.stdin.read(1)
                    finally:
                        # 恢复标准输入为原来的属性
                        termios.tcsetattr(fd, termios.TCSADRAIN, old_settings)
                    if ch:
                        breakflag = True
                else:
                    if msvcrt.getch():
                        breakflag = True
    
            # 创建线程
            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')
    
    if __name__ == '__main__':
        import argparse
        # 设置命令行参数
        parser = argparse.ArgumentParser()
        parser.add_argument('file',
                            help='Video file or charvideo file')
        parser.add_argument('-e', '--export', nargs = '?', const = 'charvideo.txt',
                            help='Export charvideo file')
        # 获取参数
        args = parser.parse_args()
        v2char = V2Char(args.file)
        if args.export:
            v2char.export(args.export)
        v2char.play()
    
    

    ·命令行用法:

    如果没有转换过

    python3 main.py video.mp4 -e  
    

    如果已经转换过,下次播放可以直接

    python3 main.py charvideo.txt
    
    展开全文
  • python图像转字符画需要用到matplotlib.pyplot库,视频转字符画需要用到opencv库,这里的代码基于python 3.5 图像转字符画需要先将图像转为灰度图,转灰度图的公式是 gray = 0.2126 * r + 0.7152 * g + 0.0722 * b,...

    python图像转字符画需要用到matplotlib.pyplot库,视频转字符画需要用到opencv库,这里的代码基于python 3.5

    图像转字符画需要先将图像转为灰度图,转灰度图的公式是 gray = 0.2126 * r + 0.7152 * g + 0.0722 * b,因为matplotlib图像的色彩排序是RGB的(opencv是BGR),所以如果不用库函数,可以使用以下代码实现灰度转换:

    gray = 0.2126 * pic[:,:,0] + 0.7152 * pic[:,:,1] + 0.0722 * pic[:,:,2]
    

    转成灰度图以后,对于每一个像素值,都要对应一个字符值。然后从图像中均匀取一些像素出来作映射即可实现图像到字符画的转换,代码如下:

    import matplotlib.pyplot as plt
    show_heigth = 30              
    show_width = 40
    #这两个数字是调出来的
    
    ascii_char = list("$@B%8&WM#*oahkbdpqwmZO0QLCJUYXzcvunxrjft/\|()1{}[]?-_+~<>i!lI;:,\"^`'. ")
    #生成一个ascii字符列表
    char_len = len(ascii_char)
    
    pic = plt.imread("wm.jpg")
    #使用plt.imread方法来读取图像,对于彩图,返回size = heigth*width*3的图像
    #matplotlib 中色彩排列是R G B
    #opencv的cv2中色彩排列是B G R
    
    pic_heigth,pic_width,_ = pic.shape
    #获取图像的高、宽
    
    gray = 0.2126 * pic[:,:,0] + 0.7152 * pic[:,:,1] + 0.0722 * pic[:,:,2]
    #RGB转灰度图的公式 gray = 0.2126 * r + 0.7152 * g + 0.0722 * b
    
    #思路就是根据灰度值,映射到相应的ascii_char
    for i in range(show_heigth):
        #根据比例映射到对应的像素
        y = int(i * pic_heigth / show_heigth)
        text = ""
        for j in range(show_width):
            x = int(j * pic_width / show_width)
            text += ascii_char[int(gray[y][x] / 256 * char_len)]
        print(text)
    

    在这里插入图片描述
    对于视频,其实也就是将每一帧转成字符画,这里用opencv来读取视频的每一帧。

    如果没有装opencv库的话,可以先去https://www.lfd.uci.edu/~gohlke/pythonlibs/下载对应版本的opencv,然后安装

    如果遇到numpy core的问题,可以更新一下numpy。

    为了有更好的显示效果,先将每一帧的转换的字符画保存下来,最后再统一输出出去。

    import cv2
    import os
    show_heigth = 30              
    show_width = 80
    
    ascii_char = list("$@B%8&WM#*oahkbdpqwmZO0QLCJUYXzcvunxrjft/\|()1{}[]?-_+~<>i!lI;:,\"^`'. ")
    #生成一个ascii字符列表
    char_len = len(ascii_char)
    
    vc = cv2.VideoCapture("v.mkv")          #加载一个视频
    
    if vc.isOpened():                       #判断是否正常打开
        rval , frame = vc.read()
    else:
        rval = False
        
    frame_count = 0
    outputList = []                         #初始化输出列表
    while rval:   #循环读取视频帧  
        gray = cv2.cvtColor(frame, cv2.COLOR_BGR2GRAY)  #使用opencv转化成灰度图
        gray = cv2.resize(gray,(show_width,show_heigth))#resize灰度图
        text = ""
        for pixel_line in gray:
            for pixel in pixel_line:                    #字符串拼接
                text += ascii_char[int(pixel / 256 * char_len )]
            text += "\n"                                
        outputList.append(text)
        frame_count = frame_count + 1                           
        if frame_count % 100 == 0:
            print("已处理" + str(frame_count) + "帧")
        rval, frame = vc.read()  
    print("处理完毕")
    
    for frame in outputList:            
        os.system("cls")                    #清屏
        print(frame)
        print()
        print()
    

    这里用的bad apple的视频来进行转换:
    在这里插入图片描述
    最后福利时间
    在这里插入图片描述
    链接:百度网盘
    提取码:fkfw
    链接容易被举报过期,如果失效了可以加群654234959自取

    展开全文
  • Python3 & OpenCV 视频转字符动画

    千次阅读 2017-02-17 15:51:24
    Python3 & OpenCV 视频转字符动画 一、实验简介 1.1. 知识点 OpenCV 编译使用 OpenCV 处理图片、视频图片转字符画原理守护线程光标定位转义编码 1.2. 效果展示 (由于是在线环境,流畅度是不及本地...

    Python3 & OpenCV 视频转字符动画

    一、实验简介

    1.1. 知识点

    • OpenCV 编译
    • 使用 OpenCV 处理图片、视频
    • 图片转字符画原理
    • 守护线程
    • 光标定位转义编码

    1.2. 效果展示

    (由于是在线环境,流畅度是不及本地环境的)

    播放停止后的效果,注意终端中并无残留的动画字符:

    此处输入图片的描述

    二、实验步骤

    2.1. 编译安装 OpenCV

    由于安装过程需要等待很长时间,实验楼已经在环境中为大家安装OpenCV,可直接使用,以下安装步骤仅作参考

    本课程的实验中使用了 OpenCV 3.1,因此我们需要编译安装它。

    首先我们需要处理一个问题:当前实验楼的环境中 python3 命令使用的 python 版本为 3.5,但源中没有 python3.5-dev 的包,这会导致编译 numpy、OpenCV 出错,以及可能会出现其它问题。总而言之,我们需要将 python3 命令使用的 python 版本切换为 3.4。

    $ sudo update-alternatives --install /usr/bin/python3 python3 /usr/bin/python3.4 70 --slave /usr/bin/python3m python3m /usr/bin/python3.4m
    

    然后安装一些依赖的包:

    $ sudo apt-get update
    $ sudo apt-get install python3-dev
    $ sudo pip3 install numpy
    $ sudo apt-get install cmake libgtk2.0-dev pkg-config libavcodec-dev libavformat-dev libswscale-dev
    

    现在可以开始编译 OpenCV 3.1 了,不过这个编译过程极其耗费时间(2个小时多),我相信各位同学们是没有耐心等待编译完成的,所以我在后面提供了编译后的二进制文件下载链接,大家直接使用就可以了。

    下面是在实验楼环境中编译 OpenCV 3.1 所需的命令,其他环境中的编译请参考官网:http://docs.opencv.org/master/d7/d9f/tutorial_linux_install.html

    $ wget https://github.com/Itseez/opencv/archive/3.1.0.zip
    $ unzip 3.1.0.zip && cd opencv-3.1.0/
    $ mkdir build && cd build
    $ cmake -D CMAKE_BUILD_TYPE=Release \
            -D CMAKE_INSTALL_PREFIX=/usr/local  \
               PYTHON3_EXECUTABLE=/usr/bin/python3 \
               PYTHON_INCLUDE_DIR=/usr/include/python3.4 \
               PYTHON_LIBRARY=/usr/lib/x86_64-linux-gnu/libpython3.4m.so \
               PYTHON3_NUMPY_INCLUDE_DIRS=/usr/local/lib/python3.4/dist-packages/numpy/core/include ..
    $ make -j4
    

    不想自己编译的同学请下载编译好的二进制文件,然后解压并进入 opencv-3.1.0/build 目录:

    $ wget http://labfile.oss.aliyuncs.com/courses/637/opencv-3.1.0.tar.gz
    $ tar xzvf opencv-3.1.0.tar.gz
    $ cd opencv-3.1.0/build
    

    然后我们开始安装:

    $ sudo make install
    

    2.2. 程序原理

    大家应该都明白视频其实可以看作一系列图片组成的,因此视频转字符动画最基本的便是图片转字符画,这一部分内容也在Python 图片转字符画 课程中有讲过。

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

    字符动画要能播放才有意义。最最简单粗暴的,用文本编辑器打开字符动画文本文件,然后狂按 PageDown 键就能播放。然而这真的太简单太粗暴了,一点都不优雅。

    我们还是在终端里面播放字符动画,只需要一帧一帧输出就能达到动画的效果了,然而这却有一个很大的弊端:播放时,你会发现终端右边的滚动条会越来越小(如果有的话);播放完毕后,在终端中往上翻页,全是之前输出的字符画,播放前的命令历史全部被挤占掉了。在本实验后面提供了这个问题的解决办法。

    2.3. 程序实现

    为了避免忘记,先导入会使用到的包:

    import sys
    import os
    import time
    import threading
    import termios
    import tty
    import cv2
    import pyprind
    

    最后一个 pyprind 模块提供显示用的进度条。因为从视频转为字符动画是一个耗时的过程,我们需要一个进度条来查看任务进度以及大致剩余时间,能更直观的了解程序状态。这样安装它:

    $ sudo pip3 install pyprind
    

    本实验将编写的程序除了能将视频文件转为字符动画并播放外,还顺便把图片文件转字符画的功能也添加上了,因此我们的程序设计有三个类:CharFrameI2CharV2Char,后两个类继承自第一个类。

    CharFrame 类:

    class CharFrame:
    
        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 可以根据你要转换的视频进行针对性调整。

    pixelToChar() 方法只有一个参数,其接收像素的亮度信息。需要注意的是,方法的 return 语句中的表达式使用了一个数值 256,虽然像素的亮度范围是 0~255,但若是把 256 更改为 255,那么这个表达式将有可能引发 IndexError 异常。

    convert() 方法有一个位置参数三个可选参数,参数 img 接收一个对象,这个对象类型是 numpy.ndarray,也就是 OpenCV 打开图片返回的对象,同样,之后用 OpenCV 得到的视频的帧也是这个对象。limitSize 参数接受一个元组,表示图片的限宽限高。fill 表示是否用空格填充图片宽度至限宽,wrap 表示是否在行末添加换行符。

    img.shape 返回一个元组,含有图片的行数(高),列数(宽),以及颜色通道数。如果图片为灰度图则不包含颜色通道数。

    cv2.resize() 函数用于缩放图片大小,第一个参数为 numpy.ndarray 对象,第二个是将缩放的宽高,interpolation 参数为插值方法。有几个插值方法可用,引用 OpenCV 官网的说明:

    Preferable interpolation methods are cv2.INTER_AREA for shrinking and cv2.INTER_CUBIC (slow) &cv2.INTER_LINEAR for zooming. By default, interpolation method used is cv2.INTER_LINEAR for all resizing purposes.

    img[i,j] 返回图片第 i 行第 j 列像素 BGR 值构成的列表,灰度图片则为直接返回对应像素的亮度值。

    下面是继承自 CharFrame 的 I2Char 类:

    class I2Char(CharFrame):
    
        result = None
    
        def __init__(self, path, limitSize=-1, fill=False, wrap=False):
            self.genCharImage(path, limitSize, fill, wrap)
    
        def genCharImage(self, path, limitSize=-1, fill=False, wrap=False):
            img = cv2.imread(path, cv2.IMREAD_GRAYSCALE)
            if img is None:
                return
            self.result = self.convert(img, limitSize, fill, wrap)
    
        def show(self, stream = 2):
            if self.result is None:
                return
            if stream == 1 and os.isatty(sys.stdout.fileno()):
                self.streamOut = sys.stdout.write
                self.streamFlush = sys.stdout.flush
            elif stream == 2 and os.isatty(sys.stderr.fileno()):
                self.streamOut = sys.stderr.write
                self.streamFlush = sys.stderr.flush
            elif hasattr(stream, 'write'):
                self.streamOut = stream.write
                self.streamFlush = stream.flush
            self.streamOut(self.result)
            self.streamFlush()
            self.streamOut('\n')
    

    cv2.imread() 读取一个图片,返回一个 numpy.ndarray 对象。第一个参数为要打开的图片路径,第二个参数指定图片的打开方式,可以具有下面三个值:

    • cv2.IMREAD_COLOR : 加载彩色图片,忽略透明通道。
    • cv2.IMREAD_GRAYSCALE : 以灰度模式加载 图片。
    • cv2.IMREAD_UNCHANGED : 加载包含透明通道的图片。

    show() 方法接受一个参数,表示使用哪种输出流。1 代表输出流使用标准输出 sys.stdout2 代表输出流使用标准错误输出 sys.stderr。其中 sys.stdout.fileno() 和 sys.stderr.fileno() 分别返回标准输出和标准错误输出的文件描述符os.isatty(fd) 返回一个布尔值,当文件描述符 fd 是打开并连接到 tty 设备时返回真。

    然后便是我们的重点了,继承自 CharFrame 类的 V2Char 类。

    先设想一下我们的类,类的其中一个属性为 charVideo ,这是一个列表,用来存放字符动画全部数据。

    然后是两个主要方法:一个是从视频文件转为字符动画的方法 genCharVideo(),一个是播放字符动画的方法 play()

    另外由于从视频转到字符动画是一个耗时耗力的过程,所以我们可以把 charVideo 中的字符动画数据导出来,方便下次读取播放,这就意味着要有导出和读取方法,即 export() 方法和 load() 方法。

    类还要有一个初始化方法,参数为要读取的文件路径,文件若是导出的 txt,则调用 load() 方法把数据加载到属性 charVideo 里。否则视为视频文件,调用 genCharVideo() 方法将视频转化为字符动画存放到属性 charVideo 里:

    def __init__(self, path):
        if path.endswith('txt'):
            self.load(path)
        else:
            self.genCharVideo(path)
    

    接下来是 genCharVideo() 方法:

    def genCharVideo(self, filepath):
        self.charVideo = []
        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)):
            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()
    

    用 cv2.VideoCapture() 方法读取视频文件,生成的对象我们赋值给 cap

    通过 cap.get() 方法我们可以获得视频的属性信息,比如 cap.get(3) 和 cap.get(4) 分别返回视频的宽高信息,cap.get(5) 则返回视频的帧率,cap.get(7) 返回视频的总帧数。

    timeInterval 存放播放时间间隔,用来让之后播放字符动画的帧率与原视频相同。

    pyprind.prog_bar() 是一个生成器,使用这个生成器进行迭代会自然在终端中输出进度条。

    cap.read() 读取视频的下一帧,其返回一个两元素的元组,第一个元素为 bool 值,指示帧是否被正确读取,第二个元素为numpy.ndarray ,其存放的便是帧的数据。

    cv2.cvtColor() 用来转换图像的颜色空间,第一个参数为图像对象,第二个参数指示转换类型,OpenCV 中有超过 150 个颜色空间转换,这里我们使用的是彩色转灰度 cv2.COLOR_BGR2GRAY

    os.get_terminal_size() 方法返回当前终端的列数(宽),行数(高)。这里我们将 fill 参数设置为 True,未设置 wrap 参数,其值为默认的 False。在终端里如果打印的字符超过一行的宽度,终端会自动进行显示上的换行。

    最后别忘了用 cap.release() 释放资源。

    然后是 play() 方法,这就要接着之前说了,要想终端不在播放字符动画后充满没用的字符,可以使用光标定位转义编码。

    我们可以这样:每输出一帧,就将光标移动到播放开始处,下一帧会从这个位置输出,并自动覆盖掉之前的内容,如此往复循环,播放完毕时,清除输出的最后一帧,这样终端就不会被字符画挤满了。

    这里是一系列用来定位光标的转义编码(某些终端不支持一些转义编码),引自 The Linux Command Line

    转义编码 行动
    \033[l;cH 把光标移到第 l 行,第 c 列。
    \033[nA 把光标向上移动 n 行。
    \033[nB 把光标向下移动 n 行。
    \033[nC 把光标向前移动 n 个字符。
    \033[nD 把光标向后移动 n 个字符。
    \033[2J 清空屏幕,把光标移到左上角(第零行,第零列)。
    \033[K 清空从光标位置到当前行末的内容。
    \033[s 存储当前光标位置。
    \033[u 唤醒之前存储的光标位置。

    还有一个问题,如何在中途停止播放?当然你可以按 Ctrl + C,但这样程序是没办法做扫尾工作的,终端会留下一堆没用的乱七八糟的字符。

    我们这样设计,字符动画开始播放时启动一个守护线程,进程启动时便开始等待用户输入,一旦接收到输入便停止播放字符动画。

    这里有两个地方需要注意:

    1. 不要使用 input() 方法接收字符输入
    2. 不能使用普通线程

    对于第一点,如果是想按任意字符终止播放的话,那么就不应该使用 input(),否则要么你按回车停止播放,要么按其他字符再敲回车停止播放,总而言之,使用 input() 并不舒服。最好的办法是,使用类似于 C 语言中的 getchar() 方法,然而 python 并没有提供类似方法,这在后面的代码中会给出替代方案。

    对于第二点,我们要明白如果任何一个派生线程仍在运行中,主线程是不会退出的,除非派生线程被设定为守护线程。所以使用普通线程的话,若用户并没有在中途停止播放,等到播放完毕后,这个线程会永远的运行下去,直到用户输入任意字符。如果我们可以在播放完毕时手动 kill 掉这个派生线程,这也不是问题,然而 python 并没有提供 kill 掉线程的方法。所以,只能把这个派生线程设为守护线程。等主线程退出后,程序只剩下一个守护线程在运行,守护线程会自动被 kill 掉,程序退出。

    下面类 play() 方法的代码:

    def play(self, stream = 1):
        # Bug:
        # 光标定位转义编码不兼容 Windows
        if not self.charVideo:
            return
        if stream == 1 and os.isatty(sys.stdout.fileno()):
            self.streamOut = sys.stdout.write
            self.streamFlush = sys.stdout.flush
        elif stream == 2 and os.isatty(sys.stderr.fileno()):
            self.streamOut = sys.stderr.write
            self.streamFlush = sys.stderr.flush
        elif hasattr(stream, 'write'):
            self.streamOut = stream.write
            self.streamFlush = stream.flush
    
        old_settings = None
        breakflag = None
        # 获得标准输入的文件描述符
        fd = sys.stdin.fileno()
    
        def getChar():
            nonlocal breakflag
            nonlocal old_settings
            # 保存标准输入的属性
            old_settings = termios.tcgetattr(fd)
            # 设置标准输入为原始模式
            tty.setraw(sys.stdin.fileno())
            # 读取一个字符
            ch = sys.stdin.read(1)
            breakflag = True if ch else False
    
        # 创建线程
        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 is True:
                break
            self.streamOut(frame)
            self.streamFlush()
            time.sleep(self.timeInterval)
            # 共 rows 行,光标上移 rows-1 行回到开始处
            self.streamOut('\033[{}A\r'.format(rows-1))
        # 恢复标准输入为原来的属性
        termios.tcsetattr(fd, termios.TCSADRAIN, old_settings)
        # 光标下移 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')
        info = 'User interrupt!\n' if breakflag else 'Finished!\n'
        self.streamOut(info)
    

    一些函数的文档链接:

    termios.tcgetattr(fd)termios.tcsetattr(fd, when, attributes)链接

    tty.setraw(fd, when=termios.TCSAFLUSH)链接

    V2Char 类的完整代码如下:

    class V2Char(CharFrame):
    
        charVideo = []
        timeInterval = 0.033
    
        def __init__(self, path):
            if path.endswith('txt'):
                self.load(path)
            else:
                self.genCharVideo(path)
    
        def genCharVideo(self, filepath):
            self.charVideo = []
            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)):
                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()
    
        def export(self, filepath):
            if not self.charVideo:
                return
            with open(filepath,'w') as f:
                for frame in self.charVideo:
                    # 加一个换行符用以分隔每一帧
                    f.write(frame + '\n')
    
        def load(self, filepath):
            self.charVideo = []
            # 一行即为一帧
            for i in  open(filepath):
                self.charVideo.append(i[:-1])
    
        def play(self, stream = 1):
            # Bug:
            # 光标定位转义编码不兼容 Windows
            if not self.charVideo:
                return
            if stream == 1 and os.isatty(sys.stdout.fileno()):
                self.streamOut = sys.stdout.write
                self.streamFlush = sys.stdout.flush
            elif stream == 2 and os.isatty(sys.stderr.fileno()):
                self.streamOut = sys.stderr.write
                self.streamFlush = sys.stderr.flush
            elif hasattr(stream, 'write'):
                self.streamOut = stream.write
                self.streamFlush = stream.flush
    
            old_settings = None
            breakflag = None
            # 获得标准输入的文件描述符
            fd = sys.stdin.fileno()
    
            def getChar():
                nonlocal breakflag
                nonlocal old_settings
                # 保存标准输入的属性
                old_settings = termios.tcgetattr(fd)
                # 设置标准输入为原始模式
                tty.setraw(sys.stdin.fileno())
                # 读取一个字符
                ch = sys.stdin.read(1)
                breakflag = True if ch else False
    
            # 创建线程
            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 is True:
                    break
                self.streamOut(frame)
                self.streamFlush()
                time.sleep(self.timeInterval)
                # 共 rows 行,光标上移 rows-1 行回到开始处
                self.streamOut('\033[{}A\r'.format(rows-1))
            # 恢复标准输入为原来的属性
            termios.tcsetattr(fd, termios.TCSADRAIN, old_settings)
            # 光标下移 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')
            info = 'User interrupt!\n' if breakflag else 'Finished!\n'
            self.streamOut(info)
    

    2.4. 测试效果

    把下面的代码键入到之前的代码后面,这份代码就可以用做视频转字符画的脚本文件了。

    if __name__ == '__main__':
        import argparse
        # 设置命令行参数
        parser = argparse.ArgumentParser()
        parser.add_argument('file',
                            help='Video file or charvideo file')
        parser.add_argument('-e', '--export', nargs = '?', const = 'charvideo.txt',
                            help='Export charvideo file')
        # 获取参数
        args = parser.parse_args()
        v2char = V2Char(args.file)
        if args.export:
            v2char.export(args.export)
        v2char.play()
    

    用如下命令把测试用视频下载下来:

    $ wget http://labfile.oss.aliyuncs.com/courses/637/BadApple.mp4
    

    假设你的代码文件为 CLIPlayVideo.py,输入如下命令将 BadApple.mp4 文件转为字符动画并导出为文件,同时播放转换后的字符动画。

    $ python3 CLIPlayVideo.py BadApple.mp4 -e
    

    之后再次播放不必重新转换一次,可以直接读取导出的字符画,假设这个文件是 charvideo.txt:

    $ python3 CLIPlayVideo.py charvideo.txt
    

    三、实验总结

    本实验使用了 OpenCV 处理图片及视频,虽然编译 OpenCV 的过程很恼人,但这也是一个必要的过程。这里的 OpenCV 的操作对于大家来说应该只是一个开始,如果想深入学习应该多看看官方文档。使用了守护线程后,相信大家能够明白守护线程是什么以及与普通线程有什么区别。最后还了解了光标定位转义编码,虽然这个用处可能并不大,但也是一个有趣的东西,它还可以做很多事情,大家可以多玩玩。

    四、完整代码

    可以直接把代码下载下来:

    $ wget http://labfile.oss.aliyuncs.com/courses/637/CLIPlayVideo.py
    

    下面贴出了完整的代码:

    import sys
    import os
    import time
    import threading
    import termios
    import tty
    import cv2
    import pyprind
    
    
    class CharFrame:
    
        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
    
    
    class I2Char(CharFrame):
    
        result = None
    
        def __init__(self, path, limitSize=-1, fill=False, wrap=False):
            self.genCharImage(path, limitSize, fill, wrap)
    
        def genCharImage(self, path, limitSize=-1, fill=False, wrap=False):
            img = cv2.imread(path, cv2.IMREAD_GRAYSCALE)
            if img is None:
                return
            self.result = self.convert(img, limitSize, fill, wrap)
    
        def show(self, stream = 2):
            if self.result is None:
                return
            if stream == 1 and os.isatty(sys.stdout.fileno()):
                self.streamOut = sys.stdout.write
                self.streamFlush = sys.stdout.flush
            elif stream == 2 and os.isatty(sys.stderr.fileno()):
                self.streamOut = sys.stderr.write
                self.streamFlush = sys.stderr.flush
            elif hasattr(stream, 'write'):
                self.streamOut = stream.write
                self.streamFlush = stream.flush
            self.streamOut(self.result)
            self.streamFlush()
            self.streamOut('\n')
    
    
    class V2Char(CharFrame):
    
        charVideo = []
        timeInterval = 0.033
    
        def __init__(self, path):
            if path.endswith('txt'):
                self.load(path)
            else:
                self.genCharVideo(path)
    
        def genCharVideo(self, filepath):
            self.charVideo = []
            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)):
                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()
    
        def export(self, filepath):
            if not self.charVideo:
                return
            with open(filepath,'w') as f:
                for frame in self.charVideo:
                    # 加一个换行符用以分隔每一帧
                    f.write(frame + '\n')
    
        def load(self, filepath):
            self.charVideo = []
            # 一行即为一帧
            for i in  open(filepath):
                self.charVideo.append(i[:-1])
    
        def play(self, stream = 1):
            # Bug:
            # 光标定位转义编码不兼容 Windows
            if not self.charVideo:
                return
            if stream == 1 and os.isatty(sys.stdout.fileno()):
                self.streamOut = sys.stdout.write
                self.streamFlush = sys.stdout.flush
            elif stream == 2 and os.isatty(sys.stderr.fileno()):
                self.streamOut = sys.stderr.write
                self.streamFlush = sys.stderr.flush
            elif hasattr(stream, 'write'):
                self.streamOut = stream.write
                self.streamFlush = stream.flush
    
            old_settings = None
            breakflag = None
            # 获得标准输入的文件描述符
            fd = sys.stdin.fileno()
    
            def getChar():
                nonlocal breakflag
                nonlocal old_settings
                # 保存标准输入的属性
                old_settings = termios.tcgetattr(fd)
                # 设置标准输入为原始模式
                tty.setraw(sys.stdin.fileno())
                # 读取一个字符
                ch = sys.stdin.read(1)
                breakflag = True if ch else False
    
            # 创建线程
            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 is True:
                    break
                self.streamOut(frame)
                self.streamFlush()
                time.sleep(self.timeInterval)
                # 共 rows 行,光标上移 rows-1 行回到开始处
                self.streamOut('\033[{}A\r'.format(rows-1))
            # 恢复标准输入为原来的属性
            termios.tcsetattr(fd, termios.TCSADRAIN, old_settings)
            # 光标下移 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')
            info = 'User interrupt!\n' if breakflag else 'Finished!\n'
            self.streamOut(info)
    
    if __name__ == '__main__':
        import argparse
        # 设置命令行参数
        parser = argparse.ArgumentParser()
        parser.add_argument('file',
                            help='Video file or charvideo file')
        parser.add_argument('-e', '--export', nargs = '?', const = 'charvideo.txt',
                            help='Export charvideo file')
        # 获取参数
        args = parser.parse_args()
        v2char = V2Char(args.file)
        if args.export:
            v2char.export(args.export)
        v2char.play()
    展开全文
  • python图像、视频转字符画  python图像转字符画需要用到matplotlib.pyplot库,视频转字符画需要用到opencv库,这里的代码基于python 3.5  图像转字符画需要先将图像转为灰度图,转灰度图的公式是gray = 0.2126 *...

    python图像、视频转字符画

      python图像转字符画需要用到matplotlib.pyplot库,视频转字符画需要用到opencv库,这里的代码基于python 3.5

      图像转字符画需要先将图像转为灰度图,转灰度图的公式是 gray = 0.2126 * r + 0.7152 * g + 0.0722 * b,因为matplotlib图像的色彩排序是RGB的(opencv是BGR),所以如果不用库函数,可以使用以下代码实现灰度转换:

    gray = 0.2126 * pic[:,:,0] + 0.7152 * pic[:,:,1] + 0.0722 * pic[:,:,2]

      转成灰度图以后,对于每一个像素值,都要对应一个字符值。然后从图像中均匀取一些像素出来作映射即可实现图像到字符画的转换,代码如下:

    import matplotlib.pyplot as plt
    show_heigth = 30              
    show_width = 40
    #这两个数字是调出来的
    
    ascii_char = list("$@B%8&WM#*oahkbdpqwmZO0QLCJUYXzcvunxrjft/\|()1{}[]?-_+~<>i!lI;:,\"^`'. ")
    #生成一个ascii字符列表
    char_len = len(ascii_char)
    
    pic = plt.imread("wm.jpg")
    #使用plt.imread方法来读取图像,对于彩图,返回size = heigth*width*3的图像
    #matplotlib 中色彩排列是R G B
    #opencv的cv2中色彩排列是B G R
    
    pic_heigth,pic_width,_ = pic.shape
    #获取图像的高、宽
    
    gray = 0.2126 * pic[:,:,0] + 0.7152 * pic[:,:,1] + 0.0722 * pic[:,:,2]
    #RGB转灰度图的公式 gray = 0.2126 * r + 0.7152 * g + 0.0722 * b
    
    #思路就是根据灰度值,映射到相应的ascii_char
    for i in range(show_heigth):
        #根据比例映射到对应的像素
        y = int(i * pic_heigth / show_heigth)
        text = ""
        for j in range(show_width):
            x = int(j * pic_width / show_width)
            text += ascii_char[int(gray[y][x] / 256 * char_len)]
        print(text)

    效果:

     

    对于视频,其实也就是将每一帧转成字符画,这里用opencv来读取视频的每一帧。

      如果没有装opencv库的话,可以先去https://www.lfd.uci.edu/~gohlke/pythonlibs/下载对应版本的opencv,然后安装

      如果遇到numpy core的问题,可以更新一下numpy。

      为了有更好的显示效果,先将每一帧的转换的字符画保存下来,最后再统一输出出去。

    import cv2
    import os
    show_heigth = 30              
    show_width = 80
    
    ascii_char = list("$@B%8&WM#*oahkbdpqwmZO0QLCJUYXzcvunxrjft/\|()1{}[]?-_+~<>i!lI;:,\"^`'. ")
    #生成一个ascii字符列表
    char_len = len(ascii_char)
    
    vc = cv2.VideoCapture("v.mkv")          #加载一个视频
    
    if vc.isOpened():                       #判断是否正常打开
        rval , frame = vc.read()
    else:
        rval = False
        
    frame_count = 0
    outputList = []                         #初始化输出列表
    while rval:   #循环读取视频帧  
        gray = cv2.cvtColor(frame, cv2.COLOR_BGR2GRAY)  #使用opencv转化成灰度图
        gray = cv2.resize(gray,(show_width,show_heigth))#resize灰度图
        text = ""
        for pixel_line in gray:
            for pixel in pixel_line:                    #字符串拼接
                text += ascii_char[int(pixel / 256 * char_len )]
            text += "\n"                                
        outputList.append(text)
        frame_count = frame_count + 1                           
        if frame_count % 100 == 0:
            print("已处理" + str(frame_count) + "帧")
        rval, frame = vc.read()  
    print("处理完毕")
    
    for frame in outputList:            
        os.system("cls")                    #清屏
        print(frame)
        print()
        print()

    这里用的bad apple的视频来进行转换:

    展开全文
  • 昨晚一朋友跟我说在网上看到了别人做的视频转字符动画,觉得很厉害,我于是也打算玩玩。今天中午花时间实现了这样一个小玩意。 顺便把过程记录在这里。 注:最新版使用了画布方式实现,和本文相比改动非常大,如果...
  • Python2和Python3的区别,以及为什么选Python3的原因

    万次阅读 多人点赞 2020-01-01 19:48:51
    我的机器学习教程「美团」算法工程师带你入门机器学习 已经开始更新了,欢迎大家订阅~ 任何关于算法、编程、AI行业知识或博客内容的问题,可以随时扫码关注公众号「图灵的猫」,加入”学习小组“,沙雕博主在线答疑...
  • Python3 * 和 ** 运算符

    万次阅读 多人点赞 2018-06-05 11:07:40
    Python 中,* 和 ** 具有语法多义性,具体来说是有三类用法。1. 算数运算* 代表乘法** 代表乘方&gt;&gt;&gt; 2 * 5 10 &gt;&gt;&gt; 2 ** 5 322. 函数参数*args 和 **kwargs 主要用于...
  • Python 3.7.4 for Windows的安装

    万次阅读 2019-08-01 19:04:17
    文章目录1、Python简介2、Python安装步骤3Python环境变量配置4、整数和浮点数 1、Python简介 Python是一款通用型的计算机程序设计语言,Python对编程人员来说是一款非常有利的工具,可以让您快速编写代码,而且...
  • import turtle as T import random import time # 画樱花的躯干(60,t) def Tree(branch, t): time.sleep(0.0005) if branch > 3: if 8 <= branch <= 12: if random.randint(0, 2) =...
  • python3 GUI

    万次阅读 2018-07-25 12:59:55
    python3 GUI 目录 用python3创建窗口并显示 修改窗口的名字 在窗口中加入标签 在窗口中加入按钮 使按钮有实际意义 添加可编辑文本框 用Tkinter实现一个简单的GUI程序,单击click按钮时会在终端打印出’hello ...
  • Python3中print函数的换行

    万次阅读 2017-03-18 22:41:05
    Python3中print函数的换行 最近看了看Python的应用,从入门级的九九乘法表开始,结果发现Python3.x和Python2.x真的是有太大的不同之处,就比如这里的换行处理,怕忘记先记下来,好了,咱移步下文—— Python2.X中的...
  • 手把手教你在Linux环境下安装Python3

    万次阅读 多人点赞 2020-03-03 11:39:34
    在上一篇文章《手把手教你启用Win10的Linux子系统(超详细)》我们已经学了如何在Win10环境下装Linux子系统了,那么这一篇文章我们将学习如何在该Linux系统下安装Python3。 首先是按Win+R键调出cmd命令窗口,然后...
  • Python3教程——Python3 用什么IDE开发工具       这也许是学习一门语言时大家都会问的一个问题,其实我个人认为无所谓哪个最好,关键在于哪个用着顺手,方便。还有很多人乐于去做排名,去看排名,哪个最好...
  • python3.5 安装python3-tk

    万次阅读 2017-07-05 19:23:17
    在python3.5下安装好matplotlib后,准备显示一张图片测试一下,但是控制台报错说需要安装python3-tk,我天真的以为直接: sudo apt-get install python3-tk就可以了呢。但是不行,说是找不到对应的资源。我就开始...
  • Python3使运行暂停的方法

    万次阅读 2017-10-19 20:33:19
    Python3中已经有很大一部分语句与Python2不互通了,运行暂停的方法也有所不同。 1、input();  这种方法不用包含模块,因此这也是最常用的一种暂停手段。  Python2中的raw_input()和input()语句在Python3中...
  • 如何在win10上同时安装python2和python3

    万次阅读 多人点赞 2017-11-19 10:40:25
    哎,其实本人已经用惯了python2,听说python3的语法有很多不一样的地方,那我之前写的算法改起来岂不是日了狗了吗?所以一直没改用python3。但是谷歌的那个TensorFlow,在windows下只能支持python3,没办法,这时候...
  • 1.查看已安装版本 终端输入如下: python2 --version #查看python2安装版本 python3 --version #查看python3安装版本... Python3和Python2是互相不兼容,但也不能卸载python2,可以将Python的指向Python3,这样...
1 2 3 4 5 ... 20
收藏数 3,183,939
精华内容 1,273,575
关键字:

python3