preface

The reading time If the degree of Front knowledge
5min 25% Python

Recently, IT was interesting to see a video published by Up Mainele Lab: how to make the character video. Why don’t you implement one of your own?

I haven’t written much Python before, just brushing through LeetCode. This is a good opportunity to learn Python again.

The effect

Let’s look at the implementation first.

Emmm, it smells like that.

Train of thought

First of all, we all know that video is essentially a quick picture by picture display, so the first step is to break the video into frames.

When the video is divided into individual images, each pixel in each image is a mix of red, green and blue primary colors. This mixing mechanism is represented by numerical values, such as RGB (255, 255, 255), which is white, and RGB (0, 0, 0), which is no color at all, which is black.

After getting three values, you can turn a single pixel into a value through certain calculation. Generally speaking, this process can be grayscale.

rgba(255, 255, 255) -> 0
Copy the code

After taking the gray value, you can map all the pixels to a character on the Hash table, thus forming a character painting.

0 - > $Copy the code

These character drawings are saved to a directory in TXT files, and then printed out in order to form a character video.

So let’s start implementing it now.

framing

We can use FFMPEG instead of frame splitting. Install with the following command:

brew install ffmpeg
Copy the code

Then use this command to break frames:

ffmpeg -i res/cxk-video.mov res/image_frames/%d.jpg
Copy the code

Res /image_frames/%d.jpg is the directory where the video is stored. %d is the number. JPG.

Generate character drawing

Here we can borrow from the Pillow library and get the RGB value of the image directly.

Install the library first:

pip3 install Pillow
Copy the code

If you are an M1 Mac, use these two commands to install it.

sudo python3 -m pip install --upgrade pip
sudo python3 -m pip install --upgrade Pillow
Copy the code

Then to implement the picture into character painting:

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("In process"+ file_name) image_path = join(image_frames_dir, file_name)'. ') [0] + '.txt'// Get the address of the TXT fileif not isfile(image_path):
            continue

        image = Image.openResize ((width, height), image.nearest);# NEAREST low-quality imageTXT = to_string(image, width, height) // Generate character textwith open(txt_path, 'w') asTxt_file: // Save the character text txt_file.write(TXT)Copy the code

Use getPiexel to get the tuple and then algorithmically generate the gray value, which is mapped to the defined array.

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) // Generate gray value unit = (256.0 + 1) / length
    return ascii_char[int(gray/unit)] // Map to charactersdef to_string(image, width, height) :
    txt = ""

    for h in range(height):
        for w in range(width):
            txt += get_char(*image.getpixel((w, h)))  Get pixel's color value
        txt += '\n' Remember to wrap the line at the end

    return txt
Copy the code

Then you can generate a lot of TXT files.

Characters of the video

Ok, this is enough to convert all images to character drawings. Now print these character drawings in sequence.

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 # use file names for comparison
        
    Get all TXT files, order them side by side
    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 out
        sleep(speed)
Copy the code

Now it is possible to print kun kun.

Tool function

The functionality is already there, but it needs to be polished a bit more if you want to make a product that others can have fun with.

First, you can add the is_ready function to determine if a character drawing has been generated.

from os import listdir

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

You can also add a clear function to clear the cache.

import glob
import os

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

Finally, make an entry file and add some parameters to customize the typed character video:

#! /usr/local/bin/python3
import argparse
from procedure import prepare, display, is_ready, clear

# get parameters
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()

    # parameters
    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('Run python3 main.py compile to compile')
        else:
            display(speed) # Output character video
Copy the code

Users can play a variety of ways:

./main.py run --speed 0.02 # control the speed in seconds, which is the default value

./main.py run --width 240 --height 100 # Control width and height, where the value is the default value
Copy the code

The last

All the complete code above is available at github-CXk-dance.

The problem of the Pillow installation may not be successful on THE Mac of M1. Readme. md also provides a solution.

By the way, recently just built a public account [write code of the sea monster], think I write good fate pay attention to it ~