前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >用 Python 把坤坤动起来

用 Python 把坤坤动起来

作者头像
写代码的海怪
发布2022-03-29 16:41:03
1.8K0
发布2022-03-29 16:41:03
举报
文章被收录于专栏:海怪的编程小屋

前言

阅读时长

用脑度

前置知识

5min

25%

Python

最近看到一个 Up 主 「Ele实验室」 发布的一个视频:字符化视频是怎么做出来的,感觉很有意思。不如自己也实现一个来玩玩?

以前也没怎么写过 Python,只用来刷过 LeetCode。正好借这个机会再学一学 Python 吧。

效果

先来看看实现效果。

Emmm,有那味了。

思路

首先,我们都知道视频本质上是一张张图片快速展示的效果,所以第一步就是将视频进行 「分帧」

当将视频分成一张张图片后,每张图片里的每个像素点都是由 「红、绿、蓝」 三原色混合而成的。而这样的混合机制就是通过数值值来表示的,比如 rgb(255, 255, 255) 就是白色,而 rgb(0, 0, 0) 则表示连个颜色都没有,也就是黑色。

拿到三种值后,可以通过一定计算将单像素变成一个值,一般来说这个过程可以是灰度化。

代码语言:javascript
复制
rgba(255, 255, 255) -> 0

拿到灰度后的值,就可以将所有像素映射到 Hash 表上的一个字符,从而形成 「字符画」

代码语言:javascript
复制
0 -> $

将这些字符画都以 txt 文件保存到一个目录,再按顺序打印出来就形成了 「字符视频」 了。

那我们现在开始实现吧。

分帧

分帧这里可以不用我们实现,直接使用 「ffmpeg」 就可以了。先用下面命令进行安装:

代码语言:javascript
复制
brew install ffmpeg

然后使用这个命令来分帧:

代码语言:javascript
复制
ffmpeg -i res/cxk-video.mov res/image_frames/%d.jpg

上面命令很容易理解:res/cxk-video.mov 是原视频,后面的 res/image_frames/%d.jpg 就是存放的路径,%d 表示数字.jpg。

生成字符画

这里要借用到 「Pillow」 这个库,可以直接获取图片的 rgb 值。

先安装一下这个库:

代码语言:javascript
复制
pip3 install Pillow

「如果你是 M1 的 Mac 电脑,需要用下面这两个命令来安装。」

代码语言:javascript
复制
sudo python3 -m pip install --upgrade pip
sudo python3 -m pip install --upgrade Pillow

然后来实现将图片变成字符画:

代码语言:javascript
复制
from os import listdir
from os.path import isfile, join

image_frames_dir = 'res/image_frames'
txt_frames_dir = 'res/txt_frames'

def prepare(width, height):
    for file_name in listdir(image_frames_dir):
        print("正在处理 " + file_name)

        image_path = join(image_frames_dir, file_name) // 获取图片地址
        txt_path = join(txt_frames_dir, file_name.split('.')[0] + '.txt') // 获取 txt 文件地址

        if not isfile(image_path):
            continue

        image = Image.open(image_path) // 获取图片
        image = image.resize((width, height), Image.NEAREST)  # NEAREST 低质量图

        txt = to_string(image, width, height) // 生成字符文本

        with open(txt_path, 'w') as txt_file: // 保存字符文本
            txt_file.write(txt)

使用 getpiexel 获取 tuple 然后通过算法生成 gray 值,再映射到定义好的数组上。

代码语言:javascript
复制
ascii_char = list("$@B%8&WM#*oahkbdpqwmZO0QLCJUYXzcvunxrjft/\|()1{}[]?-_+~<>i!lI;:,\"^`'. ")

def get_char(r, g, b, alpha=256):
    if alpha == 0:
        return ' '

    length = len(ascii_char)
    gray = int(0.2126 * r + 0.7152 * g + 0.0722 * b) // 生成灰度值
    unit = (256.0 + 1) / length
    return ascii_char[int(gray / unit)] // 映射到字符


def to_string(image, width, height):
    txt = ""

    for h in range(height):
        for w in range(width):
            txt += get_char(*image.getpixel((w, h)))  # 获取 pixel 的颜色数值
        txt += '\n' # 记得最后要换行

    return txt

然后就可以生成很多个 txt 文件了。

字符视频

好了,上面已经可以实现将所有图片转换成字符画了,下面将这些字符画顺序地打印出来就可以了。

代码语言:javascript
复制
import os
from time import sleep

def display(speed):
    def compare_file_name(file_name1, file_name2):
        index1 = int(file_name1.split('.')[0])
        index2 = int(file_name2.split('.')[0])

        return index1 - index2 # 以文件名来作对比
        
    # 获取所有 txt 文件,并排好序
    for file_name in sorted(listdir(txt_frames_dir), key=cmp_to_key(compare_file_name)):
        txt_path = join(txt_frames_dir, file_name)

        os.system('cat ' + txt_path) # cat 出来
        sleep(speed)

现在已经可以实现把坤坤打印出来了。

工具函数

虽然功能已经实现好了,但是如果要做出一个别人也能玩得嗨的产品还需要再打磨一下。

比如,可以添加 is_ready 函数来判断是否已经有生成好了的字符画。

代码语言:javascript
复制
from os import listdir

def is_ready():
    return len(listdir(txt_frames_dir)) != 0

还可以添加 clear 函数来清楚缓存。

代码语言:javascript
复制
import glob
import os

def clear():
    files = glob.glob(join(txt_frames_dir, '*'))
    for f in files:
        os.remove(f)

最后一步,做一个入口文件,添加一些参数来自定义打出字符视频:

代码语言:javascript
复制
#!/usr/local/bin/python3
import argparse
from procedure import prepare, display, is_ready, clear

# 获取参数
def get_args():
    parser = argparse.ArgumentParser()

    parser.add_argument('command', type=str, default='run')
    parser.add_argument('--width', type=int, default=240)
    parser.add_argument('--height', type=int, default=100)
    parser.add_argument('--speed', type=float, default=0.02)

    return parser.parse_args()

if __name__ == '__main__':
    args = get_args()

    # 参数
    command = args.command
    width = args.width
    height = args.height
    speed = args.speed

    if command == 'clear':
        clear()
    if command == 'compile':
        prepare(width, height)
    if command == 'run':
        if not is_ready():
            print('运行 python3 main.py compile 来编译')
        else:
            display(speed) # 输出字符视频

用户就可以有多种玩法了:

代码语言:javascript
复制
./main.py run --speed 0.02 # 控制速度,单位为 seconds,这里数值为默认值

./main.py run --width 240 --height 100 # 控制宽高,这里数值为默认值

最后

上面所有完整的代码都可以在 Github 上搜索 cxk-dance 这个仓库进行获取,各位观众老爷自行 clone 来玩吧。

在 M1 的 Mac 上有可能会出现 Pillow 安装不成功的问题,在 README.md 也给出了相应的解决办法。

本文参与 腾讯云自媒体同步曝光计划,分享自微信公众号。
原始发表:2021-07-16,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 写代码的海怪 微信公众号,前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体同步曝光计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
目录
  • 前言
  • 效果
  • 思路
  • 分帧
  • 生成字符画
  • 字符视频
  • 工具函数
  • 最后
领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档