Python-视频处理-视频转字符动画(图像处理 cv2 moviepy Image)-程序员宅基地

技术标签: python  音视频  图像处理  

一、 代码

import os
import cv2
import moviepy.editor as mp

from PIL import Image, ImageFont, ImageDraw


# 图片像素点字符数组
ASCII_CHAR = list("$B%314567890*WM#oahkbdpqwmZO0QLCJUYXzcvunxrjft/\|()1{}[]?-_+~<>i!lI;:oa+>!:+. ")
# 视频文件 .mp4 要保存的路径
SAVE_PATH = r"E:\b\node.js\complete-node-bootcamp\1-node-farm\learning"
# 视频文件 .mp4 转换为 帧图片 所在文件夹路径
CACHE_PATH = os.path.join(SAVE_PATH, "Cache")
# 帧图片 转换为 字符图片 所在文件夹路径
CHARACTER_PATH = os.path.join(SAVE_PATH, "Character")


def get_ascii(r, g, b, alpha=256):
    """
    将图片的像素点转换为字符数组中的字符
    :param r: 红
    :param g: 绿
    :param b: 蓝
    :param alpha: 亮度
    :return: 返回标准灰度数值的 ASCII_CHAR 数组
    """
    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 txt2image(file_name):
    """
    将 .jpg 图片 重新绘制为 字符图片
    :param file_name: 输出文件名称
    :return: 无返回值
    """
    img = Image.open(os.path.join(CACHE_PATH, file_name)).convert("RGB")
    img_width = img.width
    img_height = img.height
    img = img.resize((int(img.width / 100 * 100), int(img.height / 100 * 100)), Image.NEAREST)

    raw_width = img.width
    raw_height = img.height
    width = int(raw_width / 6)
    height = int(raw_height / 15)
    img = img.resize((width, height), Image.NEAREST)

    txt = ""
    colors = []
    for y in range(height):
        for x in range(width):
            pixel = img.getpixel((x, y))
            colors.append((pixel[0], pixel[1], pixel[2]))
            if len(pixel) == 4:
                txt += get_ascii(pixel[0], pixel[1], pixel[2], pixel[3])
            else:
                txt += get_ascii(pixel[0], pixel[1], pixel[2])
        txt += "\n"
        colors.append((255, 255, 255))

    img_txt = Image.new("RGB", (raw_width, raw_height), (255, 255, 255))
    new_img = ImageDraw.Draw(img_txt)
    font = ImageFont.load_default().font

    x = y = 0
    font_w, font_h = font.getsize(txt[1])
    font_h *= 1.37
    for row in range(len(txt)):
        if txt[row] == "\n":
            x += font_h
            y = -font_w
        new_img.text((y, x), txt[row], fill=colors[row])
        y += font_w

    img_txt = img_txt.resize((img_width, img_height), Image.NEAREST)
    img_txt.save(os.path.join(CHARACTER_PATH, file_name))


def video2jpg(file_path):
    """
    将 .mp4 视频文件拆分成 .jpg 帧图片
    :param file_path: 视频所在路径
    :return: 返回捕捉到的视频帧图片集
    """
    vc = cv2.VideoCapture(file_path)
    if vc.isOpened():
        r, frame = vc.read()
        if not os.path.exists(CACHE_PATH):
            os.mkdir(CACHE_PATH)
        os.chdir(CACHE_PATH)
    else:
        r = False

    convert_name = 0
    while r:
        frame = frame[0:1080, 272:2188]    # 图片裁剪
        # frame = cv2.resize(frame, (imgs_width, imgs_height))  # 图片大小重置
        cv2.imwrite(str(convert_name) + ".jpg", frame)
        txt2image(str(convert_name) + ".jpg")
        r, frame = vc.read()
        convert_name += 1

    print("当前视频 {} 转换的 .jpg 图片数量为: {}\n".format(file_path.split("\\")[-1], convert_name))

    return vc


def jpg2video(outfile_name, fps):
    """
    将图片合成视频
    :param outfile_name: 输出文件名
    :param fps: 帧频率
    :return: 无返回值
    """
    fourcc = cv2.VideoWriter_fourcc(*"MP4V")   # .mp4 格式的视频

    images = os.listdir(CACHE_PATH)
    img0 = Image.open(os.path.join(CACHE_PATH, images[0]))
    imgs_width = img0.width
    imgs_height = img0.height

    outfile_path = os.path.join(SAVE_PATH, outfile_name + ".mp4")

    vw = cv2.VideoWriter(outfile_path, fourcc, fps, (imgs_width, imgs_height))

    # os.chdir(CACHE_PATH)  # 输出经过处理后的 合成的 原来的 无声视频
    os.chdir(CHARACTER_PATH)   # 输出处理后的字符图片合成的无声视频
    for image in range(len(images)):
        frame = cv2.imread(str(image) + ".jpg")
        vw.write(frame)

    vw.release()


def video2mp3(file_path, outfile_name):
    """
    视频文件转换为音频文件
    :param file_path: 视频文件所在路径
    :param outfile_name: 输出的音频文件名
    :return: 无返回值
    """
    outfile_path = os.path.join(SAVE_PATH, outfile_name + ".mp3")
    if os.path.exists(outfile_path):
        os.remove(outfile_path)

    os.chdir(SAVE_PATH)
    video_clip = mp.VideoFileClip(file_path)
    video_clip.audio.write_audiofile(os.path.join(SAVE_PATH, outfile_name + ".mp3"))


def composite_video_and_mp3(video_path, mp3_path, outfile_name):
    """
    将视频文件和音频文件合成起来
    :param avi_file: 视频文件
    :param mp3_file: 音频文件
    :param outfile_name: 输出的视频文件名
    :return: 无返回值
    """
    outfile_path = os.path.join(SAVE_PATH, outfile_name + ".mp4")
    if os.path.exists(outfile_path):
        os.remove(outfile_path)

    os.chdir(SAVE_PATH)
    video_clip = mp.VideoFileClip(video_path)
    mp3_clip = mp.AudioFileClip(mp3_path)
    composite_clip = video_clip.set_audio(mp3_clip)
    composite_clip.write_videofile(outfile_path)


def remove_dir(file_path):
    """
    删除中间文件夹
    :param path: 文件夹路径
    :return: 无返回值
    """
    if os.path.exists(file_path):
        if os.path.isdir(file_path):
            files = os.listdir(file_path)
            for file in files:
                path = os.path.join(file_path, file)
                if os.path.isdir(path):
                    remove_dir(path)
                elif os.path.isfile(path):
                    os.remove(path)
            os.rmdir(file_path)

            return

        elif os.path.isfile(file_path):
            os.remove(file_path)

        return


if __name__ == '__main__':

    # 视频文件所在路径
    file_path = r"E:\b\node.js\complete-node-bootcamp\1-node-farm\learning\op.mp4"
    # 视频文件名
    file_name = file_path.split("\\")[-1]

    print("*********   开始视频转换    *********\n")
    print("*********   1. 视频转换为图片    *********\n")
    vc = video2jpg(file_path)
    FPS = vc.get(cv2.CAP_PROP_FPS)
    vc.release()
    print("当前视频 {} 转换为图片成功!\n".format(file_name))
    # exit()

    print("*********   2. 图片转换为视频    *********\n")
    outfile_name = file_path.split("\\")[-1].split(".")[0]
    print(outfile_name + ".mp4 开始生成...\n")
    jpg2video(outfile_name, FPS)
    print(outfile_name + ".mp4 生成完毕!\n")
    # exit()

    print("*********   3. 视频转换为音频    *********\n")
    print(outfile_name + ".mp3 开始生成...\n")
    video2mp3(file_path, outfile_name)
    print("\n" + outfile_name + ".mp3 生成完毕!\n")
    # exit()

    print("*********   4. 视频和音频合成为视频    *********\n")
    print(outfile_name + ".mp4 开始合成...\n")
    composite_video_and_mp3(os.path.join(SAVE_PATH, outfile_name + ".mp4"),
                            os.path.join(SAVE_PATH, outfile_name + ".mp3"),
                            outfile_name + "_new")
    print(outfile_name + ".mp4 合成完毕!\n")
    print("当前视频 {} 转换为新视频 {} 成功!\n".format(file_name, outfile_name + "_new" + ".mp4"))
    # exit()

    remove_dir(CACHE_PATH)
    remove_dir(CHARACTER_PATH)
    

二、示例

op_new

版权声明:本文为博主原创文章,遵循 CC 4.0 BY-SA 版权协议,转载请附上原文出处链接和本声明。
本文链接:https://blog.csdn.net/weixin_45454897/article/details/130706525

智能推荐

Arduino IDE for ESP8266教程(二) 创建WIFI AP模式-程序员宅基地

文章浏览阅读4k次。创建WIFI热点#include <ESP8266WiFi.h>void setup() { Serial.begin ( 115200 ); Serial.println(""); // 设置内网 IPAddress softLocal(192,168,128,1); // 1 设置内网WIFI IP地址 IPAddress..._用arduino中esp8266在wifi的ap模式设置新ip

xshell安装错误:-1605这个操作只对当前安装的产品有效_错误1605这个操作只对当前安装的产品有效-程序员宅基地

文章浏览阅读2.2k次,点赞2次,收藏2次。解决xshell安装错误:-1605这个操作只对当前安装的产品有效的问题_错误1605这个操作只对当前安装的产品有效

Cas单点登录+oauth2第三方授权要点记录_cas4集成oauth认证-程序员宅基地

文章浏览阅读1.7k次。Cas单点登录+oauth2第三方授权总结需求:由于系统需要同时支持第三方登录和单点登录,而旧代码的业务逻辑繁琐,难以修改,单点登录有问题。所以对cas和oauth2模块进行了重构。在本次开发过程中,cas工程不依赖于ouath2,先从cas构建开始总结。Cas工程的构建依赖于cas-overlay-template进行。该工程中集成了cas-server,使用该覆盖模板,我们可以在不..._cas4集成oauth认证

yarn : 无法加载文件 C:\Users\Administrator\AppData\Roaming\npm\yarn.ps1,因为在此系统上禁止运行 脚本。-程序员宅基地

文章浏览阅读517次。最近在安装yarn的时候,突然给我报了这个错,试了好久才发现是这个问题yarn : 无法加载文件 C:\Users\Administrator\AppData\Roaming\npm\yarn.ps1,因为在此系统上禁止运行 脚本。有关详细信息,请参阅 https:/go.microsoft.com/fwlink/?LinkID=135170 中的 about_Execution_Po licies。所在位置 行:1 字符: 1+ yarn+ ~~~~已解决:报错是因为没有权限,只需要指_yarn : 无法加载文件 c:\users\administrator\appdata\roaming\npm\yarn.ps1,因为

解决Linux下pycharm无法输入中文的问题_linux中的pycharm无法打中文-程序员宅基地

文章浏览阅读6k次,点赞5次,收藏12次。本方法测试于Manjaro 18.1.5,理论上使用于所有Arch Linux或其他Linux发行版。并且也应该使用于idea等其他jetbraints IDE。问题描述虽然pycharm、idea等IDE官方只有英文版,但其实它们也会读取系统的地区和语言设置,并因此而导致了一些字体显示,文字输入之类的问题。在我的系统中,虽然我已经把中文设置为的默认语言、显示语言,但是不知道Manjaro抽..._linux中的pycharm无法打中文

CSDN Markdown 图片排版与缩放显示_csdn图片并列排版代码-程序员宅基地

文章浏览阅读5.2k次,点赞7次,收藏8次。CSDN Markdown 图片排版与缩放显示_csdn图片并列排版代码

随便推点

论文阅读--Semantic Grouping Network for Video Captioning-程序员宅基地

文章浏览阅读1.1k次,点赞3次,收藏4次。Semantic Grouping Network for Video Captioningabstract提出了Semantic Grouping Network(SGN)网络:用部分已经解码的字幕中,选择可可以区分的单词短语对视频帧进行分组,也就是将表达不同意思的帧分组(与聚类相似);对语义对齐的组进行解码,以预测下一个单词;(根据前面的已经生成的、分好组的词预测下一个)以前:丢弃或者合并重复视频信息SGN:检索最有鉴别能力的单词短语,然后将这些词与视频帧关联 。这样可以让语义差不多的帧_semantic grouping network for video captioning

子查询-事务-存储引擎-变量-视图-分支结构_子查询放在内存存储引擎里-程序员宅基地

文章浏览阅读114次。## -- 子查询:-- 在一个select语句中,嵌入了另一个select语句,那么被嵌入的select语句被称为子查询语句,-- 主查询:-- 主要的查询对象,第一条select语句-- 二者关系:-- 1,子查询是嵌入到主查询中-- 2,子查询是辅助主查询的,要么充当了条件,要么充当了数据源-- 子查询是有一个可以独立存在的语句,是一个完整的select语句-- 子查询的分类:(按照结果集的行列数分)-- 1,标量子查询:返回的结果是一个数据(一行一列)-- 2,列._子查询放在内存存储引擎里

vs2008 fatal error LNK1104: cannot open file 'kernel32.lib'问题分析_错误2error lnk1104: 无法打开文件“kernel32.lib”c:\users\8a4-程序员宅基地

文章浏览阅读4.1k次。前几日,重新做了下系统win7_错误2error lnk1104: 无法打开文件“kernel32.lib”c:\users\8a408\documents\

RTX3090配置CUDA、CuDnn、PyTorch、TensorFlow版本问题_rtx3090 cuda tensorflow python版本问题-程序员宅基地

文章浏览阅读1.6w次,点赞4次,收藏41次。寒假配置了RTX3090显卡,虽然代码加了 os.environ[“CUDA_VISIBLE_DEVICES”] = “0” ,但是一直无法使用GPU运算,最后发现是CUDA与显卡版本不兼容的问题,本文列出了我的电脑目前的CUDA、CuDnn、PyTorch、TensorFlow版本及下载安装方式,给大家提供参考。1.anaconda。在原有的基础上安装了新版本的PyTorch之后,import的时候会报错,百度一下之后发现原因是新安装的PyTorch和已有的名字重复,几次uninstall无法解决之后_rtx3090 cuda tensorflow python版本问题

Ubuntu 18.04.2 LTS系统,双击.sh文件运行***.sh文件_ubuntu18.04执行.sh文件没有任何提示,也没执行过程-程序员宅基地

文章浏览阅读1.3k次。转自:https://blog.csdn.net/qq_35485061/article/details/90085676如何在Ubuntu 18.04.2 LTS系统环境下运行,双击一个文件(快捷方式),运行一个程序,或者打开一个软件呢?1,首先,打开gedit软件,编写程序,如:gedit保存为test.sh文件。2选中文件,然后在屏幕左上方点“文件”,选择首选项》行为》选择运行,如下图3,右键文件test.sh,选择属性》权限,勾选允许作为程序执行文件。4,双..._ubuntu18.04执行.sh文件没有任何提示,也没执行过程

java中比较日期大小,Calendar设置时分秒,HOUR和HOUR_OF_DAY的区别-程序员宅基地

文章浏览阅读2.6w次,点赞4次,收藏11次。Calendar calendar = Calendar.getInstance();calendar.set(Calendar.HOUR, -12);calendar.set(Calendar.MINUTE, 0);calendar.set(Calendar.SECOND, 0);Date curTime = calendar.getTime();System.out.println(..._hour_of_day

推荐文章

热门文章

相关标签