当前位置:   article > 正文

简单实现视频转化为字符视频_视频流字符串

视频流字符串

最简单的从零实现正常视频转字符视频

把大象关进冰箱需要三步

  1. 将视频按帧提取为图片序列
  2. 将图片依次转化为字符图片
  3. 将字符图片按帧重新合称为视频

环境配置

本项目主要利用视频处理库 moviepy 来读取处理视频,利用图片处理库 PIL 来处理图片(视频的每一帧)

pip install moivepy
# 也可利用万能安装模板来加速
pip --default-timeout=100 install moviepy -i https://pypi.tuna.tsinghua.edu.cn/simple
  • 1
  • 2
  • 3

关于库的使用,可以参考这篇文章:moviepy 中文文档PIL 图像基本处理,本文也会将涉及到的用法讲解出来。

  • 关于第一步,直接使用 moviepy 库的相关函数调用即可
  • 关于图片转字符,只需要按照像素遍历该图片,然后再将其按灰度值映射为字符,主要利用 PIL 库函数对 numpy 矩阵进行操作
  • 关于图片按帧重组视频,其实第二、三步是同时的,利用 moviepy 的特效设置方法,对视频的每一帧进行“图片转文字”特效即可。
代码细节
  • 利用 moviepy 的 VideoFileClip 方法对视频进行加载;
  • 利用 frame2char 函数作为特效函数,对目标帧的图片进行处理
    • ImageDraw 负责字符重画
    • ImageFont 负责绘画字符和字体控制
具体实现

利用类对该方法进行包装

# -*- coding: utf-8 -*-

import os
import os.path as osp

import argparse

import numpy as np
from moviepy.editor import *
from PIL import Image, ImageFont, ImageDraw


class Converter:
    def __init__(self, video_input_path, video_output_path, chars_width, fps, isGray, time_start=0, time_end=None):
        self.video_path = video_input_path
        self.video_name = osp.basename(video_input_path).split('.')[0]
        self.output_path = video_output_path if video_output_path else osp.dirname(video_input_path)
        self.output_path = osp.join(self.output_path, self.video_name + "_char2.mp4")
        self.isGray = isGray
        self.vfc = VideoFileClip(self.video_path).subclip(time_start, time_end)
        self.fps = fps
        self.chars_width = chars_width
        self.chars_height = int(self.chars_width / self.vfc.aspect_ratio)
        self.vfc = self.vfc.resize((self.chars_width, self.chars_height))
        font_fp = "DroidSansMono.ttf"
        self.font = ImageFont.truetype(font_fp, size=14)
        self.font_width = sum(self.font.getsize('a')) // 2

        self.video_size = int(self.chars_width * self.font_width), int(self.chars_height * self.font_width)

        # 值越大,映射的字母越靠后,表示像素更"白"
        self.pixels = \
            "$#@&%ZYXWVUTSRQPONMLKJIHGFEDCBA098765432?][}{/)(><zyxwvutsrqponmlkjihgfedcba*+1-."
        #    "$@B%8&WM#*oahkbdpqwmZO0QLCJUYXzcvunxrjft/\|()1{}[]?-_+~<>i!lI;:,\"^`'. "

    def gray2char(self, gray):
        """通过灰度值获取当前像素的字符"""
        percent = float(gray) / 255
        index = int(percent * (len(self.pixels) - 1))
        return self.pixels[index]

    def frame2char(self, t):
        """
        将每一帧的图片转化为字符
        本函数作为 moviepy.editor.VideoClip 特效功能的入口参数
        相当于对时间 t 内的视频做转为字符的特效处理
        :return: numpy.array 类型字符列表
        """
        img_np = self.vfc.get_frame(t)
        img = Image.fromarray(img_np, 'RGB')
        img_gary = img.convert(mode='L')

        img_char = Image.new('RGB', self.video_size, color='white')
        brush = ImageDraw.Draw(img_char)

        white = (255, 255, 255)
        black = (0, 0, 0)

        for y in range(self.chars_height):
            for x in range(self.chars_width):
                r, g, b = img_np[y][x] if not self.isGray else black

                gray = img_gary.getpixel((x, y))
                char = self.gray2char(gray)

                position = x * self.font_width, y * self.font_width
                brush.text(position, char, fill=(r, g, b), font=self.font)

        # img_char = img_char if not self.isGray else img_char.convert(mode='L')
        return np.array(img_char)

    def generate(self):
        """生成字符对象"""
        clip = VideoClip(self.frame2char, duration=self.vfc.duration)
        clip = clip.set_fps(self.vfc.fps).set_audio(self.vfc.audio)
        clip.write_videofile(self.output_path)

def main():
    parser = argparse.ArgumentParser(description='Convert video to char-based video.')
    parser.add_argument('-i', type=str, help='the input path of source video, such as xx/xx/xx.mp4')
    parser.add_argument('-o', type=str, default=None, help='the output path of char-based video, default is input path')
    parser.add_argument('-fps', type=int, default=10, help='the frame per second of output video, default is 8')
    parser.add_argument('-cw', type=int, default=200, help='the chars width of chars in video, default is 80')
    parser.add_argument('-g', action='store_true', default=False, help='output the gray video, default is False')
    args = parser.parse_args()
    # args.g = True
    converter = Converter(args.i, args.o, args.cw, args.fps, args.g)
    converter.generate()


if __name__ == '__main__':
    main()

  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38
  • 39
  • 40
  • 41
  • 42
  • 43
  • 44
  • 45
  • 46
  • 47
  • 48
  • 49
  • 50
  • 51
  • 52
  • 53
  • 54
  • 55
  • 56
  • 57
  • 58
  • 59
  • 60
  • 61
  • 62
  • 63
  • 64
  • 65
  • 66
  • 67
  • 68
  • 69
  • 70
  • 71
  • 72
  • 73
  • 74
  • 75
  • 76
  • 77
  • 78
  • 79
  • 80
  • 81
  • 82
  • 83
  • 84
  • 85
  • 86
  • 87
  • 88
  • 89
  • 90
  • 91
  • 92
  • 93
声明:本文内容由网友自发贡献,不代表【wpsshop博客】立场,版权归原作者所有,本站不承担相应法律责任。如您发现有侵权的内容,请联系我们。转载请注明出处:https://www.wpsshop.cn/w/知新_RL/article/detail/837814
推荐阅读
相关标签
  

闽ICP备14008679号