【Python实现代码视频/视频转字符画/代码风格视频】

news2024/9/22 1:17:58

该程序改良自GitHub开源项目VideoCharDraw
在源程序CharDraw_thread.py 带压缩和多线程版本字符画的基础上使用Tkinter库添加了图形化的操作,使用户操作体验更方便。
在这里插入图片描述
在这里插入图片描述

什么是视频字符画?

视频转字符画是一种将视频中的每一帧图像转换为由字符组成的图像表示的技术。通过将图像的像素信息映射到特定的字符集合中,可以用字符来近似地表示图像的内容。

在这个过程中,通常会对图像进行灰度化处理,然后根据像素的灰度值选择相应的字符来替代。较暗的像素可能会被映射为一些较密集的字符,而较亮的像素则可能会被映射为较稀疏的字符。

这样处理后的每帧图像看起来就像是由字符组成的画,将这些字符画按顺序组合起来,就可以形成一个视频的字符画版本。这种转换可以用于创造独特的视觉效果,或者在一些情况下,如低带宽环境或特殊的艺术表达中,用于减少视频的数据量或展示视频的基本内容。

视频字符画举例

比较典型的,耳熟能详的比如烂苹果
在这里插入图片描述
小黑子等
在这里插入图片描述

使用Python实现代码转字符画

import cv2
import os
import numpy as np
from PIL import Image, ImageDraw, ImageFont
import threading
import tkinter as tk
from tkinter import filedialog
from tkinter import simpledialog

def select_file_path(): #选择视频路径
    root = tk.Tk()
    root.withdraw()  # 隐藏主窗口

    video_path = filedialog.askopenfilename()  # 选择文件

    if video_path:
        print(f"Selected file path: {video_path}")
        return video_path
    else:
        print("No file selected.")

def get_user_input(): # 线程数,建议CPU线程数-1
    root = tk.Tk()
    root.withdraw()  # 隐藏主窗口
    thread_num = simpledialog.askinteger("输入线程", "请输入一个整数作为线程数:(建议设置为CPU线程数-1)")

    if thread_num is not None:
        print(f"User input: {thread_num}")
        return thread_num
    else:
        print("No input provided.")
video_path = select_file_path()
thread_num = get_user_input()
out_path = "VideoTestOut/"  # 输出目录
huaZhi = 1  # 清晰度,最低1,无上限

# -----以下为程序使用变量-----#
video_info = []

num = 0

info = []
'''
图片转换成字符里面的相关大小,
为元组,第一个是resize的宽高,第二个是图片输出的宽高
'''

video = cv2.VideoCapture(video_path)

# 定义一个线程安全的队列来存储待处理的图片
image_queue = []

# 获取视频信息
def getVideoInfo() -> list:
    ret = []

    (major_ver, minor_ver, subminor_ver) = cv2.__version__.split('.')
    if int(major_ver) < 3:
        fps = video.get(cv2.cv.CV_CAP_PROP_FPS)
    else:
        fps = video.get(cv2.CAP_PROP_FPS)

    ret.append(fps)  # 视频帧数
    ret.append(video.read()[1].shape[0])  # 视频高度
    ret.append(video.read()[1].shape[1])  # 视频宽度
    return ret

# 获取视频所有截图
def outVideoAllCapture():
    # 判断载入视频是否可以打开
    ret = video.isOpened()
    global num
    # 循环读取视频帧
    while ret:
        num = num + 1
        # 进行单张图片的读取,ret的值为True或者Flase,frame表示读入的图片
        ret, frame = video.read()
        if ret:
            cv2.imwrite(out_path + str(num) + '.jpg', frame)
            image_queue.append(out_path + str(num) + '.jpg')  # 将图片路径加入队列
            cv2.waitKey(1)
        else:
            break

# 单张图片转换成字符画
def imageToChar(filename, number):
    # 字符列表
    ascii_char = list("$@B%8&WM#*oahkbdpqwmZO0QLCJUYXzcvunxrjft/|()1{}[]?-_+~            <>i!lI;:,\"^`'. ")
    # 判断图片是否存在
    if os.path.exists(filename):
        # 将图片转化为灰度图像,并重设大小
        img_array = np.array(Image.open(filename).resize(info[0], Image.LANCZOS).convert('L'))  # resize里面 宽, 高 输出宽高/7
        # 创建新的图片对象
        img = Image.new('L', info[1], 255)  # 宽, 高
        draw_object = ImageDraw.Draw(img)
        # 设置字体
        font = ImageFont.truetype('consola.ttf', 10, encoding='unic')
        # 根据灰度值添加对应的字符
        for j in range(info[0][1]):  # 是resize的高
            for k in range(info[0][0]):  # 宽
                x, y = k * 8, j * 8
                index = int(img_array[j][k] / 4)
                draw_object.text((x, y), ascii_char[index], font=font, fill=0)
        # 保存字符图片
        img.save(out_path + str(number) + "g.jpg", 'JPEG')
        cv2.imwrite(out_path + str(number) + "g.jpg", cv2.imread(out_path + str(number) + "g.jpg"),
                    [cv2.IMWRITE_JPEG_QUALITY, 2])
        os.remove(out_path + str(number) + '.jpg')  # 删除原始图片
        print("已成功把第" + str(number) + "帧转换成字符画")

# 工作线程函数
def worker():
    while True:
        if image_queue:
            filename = image_queue.pop(0)
            number = int(os.path.splitext(os.path.basename(filename))[0])
            imageToChar(filename, number)
        else:
            break

def mergeImage():
    print("开始将图片合并成MP4视频")
    # global num
    videoWriter = cv2.VideoWriter(out_path + 'out.mp4', cv2.VideoWriter_fourcc('m', 'p', '4', 'v'), video_info[0],
                                  info[1])
    for i in range(1, num):
        filename = out_path + str(i) + 'g.jpg'
        if os.path.exists(filename):
            img = cv2.imread(filename=filename)
            cv2.waitKey(100)
            videoWriter.write(img)
    print("完成图片合并成MP4视频")

def deleteImg():
    print("开始删除转换图片")
    global num
    for i in range(1, num):
        os.remove(out_path + str(i) + 'g.jpg')
    print("删除转换图片完毕")

if __name__ == "__main__":
    if not os.path.exists(out_path):  # 如果没有这个输出目录就创建
        os.makedirs(out_path)

    video_info = getVideoInfo()  # 获取视频信息
    info.append((int(video_info[2] * huaZhi / 8), int(video_info[1] * huaZhi / 8)))  # 添加计算设置数值
    info.append((int(video_info[2] * huaZhi / 8) * 8, int(video_info[1] * huaZhi / 8) * 8))  # 添加计算设置数值
    # print(info)

    outVideoAllCapture()  # 截取视频所有帧

    threads = []
    for _ in range(thread_num):  # 创建多个工作线程
        t = threading.Thread(target=worker)
        t.start()
        threads.append(t)

    for t in threads:  # 等待所有工作线程完成
        t.join()

    mergeImage()  # 合并图片成视频
    deleteImg()  # 删除每一帧的字符画

    video.release()

代码解析

该程序的主要功能是将视频转换为字符画视频,具体实现步骤如下:

  1. 选择视频路径和线程数
    • 通过select_file_path函数使用tkinterfiledialog模块弹出文件选择对话框,让用户选择视频文件,并返回视频路径。
    • 通过get_user_input函数使用tkintersimpledialog模块弹出输入对话框,让用户输入线程数,并返回线程数。
  2. 设置输出目录和清晰度
    • 定义输出目录out_path为"VideoTestOut/"。
    • 定义清晰度huaZhi为1。
  3. 获取视频信息
    • getVideoInfo函数中,根据cv2的版本获取视频的帧率、高度和宽度信息。
  4. 截取视频所有帧
    • outVideoAllCapture函数中,读取视频帧并保存为图片,同时将图片路径添加到image_queue队列中。
  5. 单张图片转换成字符画
    • imageToChar函数中,对每张图片进行灰度化处理,然后根据灰度值选择相应的字符来表示图像内容,并保存为字符图片。
  6. 工作线程处理
    • 创建多个工作线程,每个线程从image_queue中取出图片路径,调用imageToChar函数进行转换。
  7. 合并图片成视频
    • mergeImage函数中,将转换后的字符图片合并成MP4视频。
  8. 删除转换图片
    • deleteImg函数中,删除转换过程中生成的字符图片。

代码实现原理主要包括以下几个方面:

  1. 视频读取和处理
    • 使用cv2.VideoCapture读取视频,通过循环逐帧读取视频并保存为图片。
    • 对图片进行灰度化和重设大小处理,然后根据灰度值将像素映射为字符,生成字符图片。
  2. 多线程处理
    • 使用线程安全的队列image_queue存储待处理的图片路径。
    • 创建多个线程,每个线程从队列中取出图片路径进行处理,提高转换效率。
  3. 图片合并和视频生成
    • 使用cv2.VideoWriter将字符图片合并成MP4视频。
  4. 文件管理
    • 在输出目录中创建必要的文件夹和文件,保存图片和视频。
    • 删除转换过程中生成的中间文件,如原始图片和字符图片。

其他

输出文件压缩
在函数imageToChar里面的倒数第三行有一个

[cv2.IMWRITE_JPEG_QUALITY, 2]

这个是压缩的格式,后面那个2可以变动,范围是1-100以内
0的话就是死命压缩,不过画质不太好
如果想要看清楚字符的话可以考虑把这个调高一点,但相对应的
输出图片所占硬盘大小也会很高
如果你不想删除输出的图片的话,把倒数第四行那个daleteImg()给他注释掉就行

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/2037242.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

Java设计模式-抽象工厂模式-一次性理解透

1. 抽象工厂模式简介 抽象工厂设计模式是创建型模式之一。抽象工厂模式与工厂模式几乎相似&#xff0c;只是它更像工厂中的工厂。 如果您熟悉Java 中的工厂设计模式&#xff0c;或看过上一篇我写的“java简单工厂模式”&#xff0c;您会注意到我们有一个工厂类。此工厂类根据…

WPF篇(18)-DataGrid数据表格控件+ComboBox下拉框控件

DataGrid数据表格控件 DataGrid是一个可以多选的数据表格控件。所以&#xff0c;它继承一个支持多选的父类——MultiSelector。 public abstract class MultiSelector : Selector {protected MultiSelector();public IList SelectedItems { get; }protected bool CanSelectMu…

Animetronic - hackmyvm

简介 靶机名称&#xff1a;Animetronic 难度&#xff1a;简单 靶场地址&#xff1a;https://hackmyvm.eu/machines/machine.php?vmAnimetronic 本地环境 虚拟机&#xff1a;vitual box 靶场IP&#xff08;Animetronic&#xff09;&#xff1a;192.168.130.188 windows_…

Golang 为何如此之快:性能分析

Golang 是一种相对年轻但很流行的语言&#xff0c;IBM 和 Google 等公司都在使用这种语言。在本文中&#xff0c;我们将了解 Golang 的特别之处。 Golang 被认为是世界上编译速度最快的语言之一。谷歌、Uber 和 Twitch 等科技公司都喜欢使用它&#xff0c;并依靠它进行内部开发…

HarmonyOS(50) 截图保存功能实现

componentSnapshot实现截图 前言权限配置和申请权限配置权限申请 componentSnapshot截图实现将PixelMap转换成图片格式保存截图到系统相册保存截图到应用沙箱全部源码参考资料 前言 HarmonyOS提供了componentSnapshot实现组件截图功能&#xff0c;可以将UI截图成为image.Pixel…

WMS助力企业数字化转型(七)

WMS系统可以帮助企业实现更精确的库存控制&#xff0c;避免库存积压和缺货现象。通过对历史数据的分析&#xff0c;企业可以预测需求趋势&#xff0c;优化库存结构&#xff0c;从而减少资金占用和运营成本。同时&#xff0c;WMS还支持与其他系统的无缝对接&#xff0c;如企业资…

限时营销与开源AI智能名片O2O商城小程序的深度融合:重塑线上促销策略的新视角

摘要&#xff1a;在数字化营销日益激烈的今天&#xff0c;限时促销活动作为吸引用户注意力、激发购买欲望的传统手段&#xff0c;面临着前所未有的挑战。随着线上营销活动的泛滥&#xff0c;消费者对传统折扣策略已逐渐产生疲劳与免疫。因此&#xff0c;探索一种更加高效、精准…

开放式耳机最不伤耳吗?舒适度高的几款精选蓝牙耳机

开放式耳机对耳朵的伤害相对较小&#xff0c;但不能说它是最不伤耳的耳机。 与传统入耳式耳机相比&#xff0c;开放式耳机的优点在于不会深入耳道&#xff0c;减少了对耳朵的压迫感和耳道内的压力&#xff0c;佩戴起来更加舒适。同时&#xff0c;开放式设计允许空气流通&#…

免费简单的制作3D卡通建模——Fuse软件和Readyplayer的使用介绍

最终效果 文章目录 最终效果一、使用Fuse软件去Steam下载安装捏人选择身体部位自定义人物细节参数换装贴图修改导出OBJ文件即可 二、使用ReadyplayerReadyplayer官网地址选择从模板开始&#xff0c;或者拍照选择图片进行捏脸将模型导入Unity通过Readyplayer官方插件导入模型通过…

白盒测试-发送请求-引出MockMvc源码类

白盒测试是什么&#xff1f; 一般是测开做白盒测试&#xff0c;研发做白盒 spring boot是和junit结合 原本是jmeter发请求&#xff0c;是看不到代码逻辑&#xff0c;有接口信息就可以实现 用测试代码发请求&#xff0c;能看到代码逻辑&#xff0c;比接口测试更全面&#xf…

大恒相机通过Line2或Line3直接给出3.3V触发,形成分时曝光

大恒相机通过Line2或Line3直接给出3.3V触发&#xff0c;形成分时曝光 一、分时曝光需求二、3.3V信号分时曝光设计 写在前面 上班了&#xff0c;没多少时间再去精度论文了&#xff0c;大多是项目上的事情。 一、分时曝光需求 一般的12V光源通过光源控制器与大恒相机Line1线连接…

PDF转换器推荐:轻松将图片批量转为PDF

高质量的图片与文档管理已经逐渐成为了我们日常工作中不可或缺的一部分。为了防止图片在传输的过程中被压缩&#xff0c;我经常将他们转换为PDF格式。这次我给你推荐几个我常用的图片转PDF的小工具吧。 1.福昕PDF转换大师 链接一下>>https://www.pdf365.cn/pdf2word/ …

关于自己部署AI大模型踩的坑(一)——硬件篇

最近一直在研究如何打算属于我自己的J.A.R.V.I.S.&#xff08;钢铁侠中的机器人管家&#xff09;。 上一篇写了我最近在部署自己的大模型&#xff0c;使用llama3.1&#xff0c; 和通义千问2。虽然最终结果也是成功了&#xff0c;过程却十分地坎坷。 所以这一篇文章一是总结其中…

keepalived基础

目录 1 高可用集群简介 1.1 高可用的概念 1.2 常见的 HA 集群 1.3 高可用集群软件 2 keepalived的功能与用途 2.1 LVS directors failover功能 2.2 LVS cluster nodes healthchecks功能 3 VRRP协议介绍 4 Keepalived 架构 5 原理总结 1 高可用集群简介 1.1 高可用的概念 高可…

数据埋点系列 5|数据驱动决策:形成数据驱动文化

在过去的几篇文章中&#xff0c;我们深入探讨了数据埋点、数据质量保证、数据分析和可视化等主题。现在&#xff0c;让我们站在更高的视角&#xff0c;讨论如何将这些技术和方法整合到实际的业务决策中&#xff0c;以及如何在组织中建立真正的数据驱动文化。 目录 1. 回顾&am…

Github 2024-08-14 C开源项目日报Top10

根据Github Trendings的统计,今日(2024-08-14统计)共有10个项目上榜。根据开发语言中项目的数量,汇总情况如下: 开发语言项目数量C项目10Objective-C项目1PHP项目1Python项目1PHP:流行的Web开发脚本语言 创建周期:4710 天开发语言:C, PHP协议类型:OtherStar数量:37340 …

【Pyspark-驯化】一文搞懂Pyspark修改hive表描述以及增加列使用技巧

【Pyspark-驯化】一文搞懂Pyspark修改hive表描述以及增加列使用技巧 本次修炼方法请往下查看 &#x1f308; 欢迎莅临我的个人主页 &#x1f448;这里是我工作、学习、实践 IT领域、真诚分享 踩坑集合&#xff0c;智慧小天地&#xff01; &#x1f387; 相关内容文档获取 微…

C++11 STL中的Vector使用细节

容器 支持泛型 Vector常用成员函数示例迭代器操作插入和删除操作 与find 配合:vector 一些复杂操作 Vector 的内存管理策略压入对象 需要无参构造.压入对象指针 高效插入和删除 迭代器失效 代码优化: 二维及多维空间生成 容器 支持泛型 vector<int> vi;vector<double…

中国数据库的前世今生:披荆斩棘,乘风破浪

文章目录 前言国外数据库技术蓬勃发展中国信息化起步与发展&#xff08;数据库技术探索&#xff09;国外数据库商战策略解决燃眉之急学习先进技术 数据库技术的新格局雷声大雨点小的千年虫新型数据库的诞生国产数据库展露头脚 开源助力国产数据库弯道超车去“IOE”化大数据席卷…

使用OIDC登录kubesphere遇到的坑细节

1.通过代理telepresence到本地调试&#xff0c;使用默认账号密码&#xff0c;提示账号密码错误。 2.kubesphere在sso登录的时候&#xff0c;提示签名错误&#xff0c;其实这个错误不是很明确&#xff0c;所以要到本地调试&#xff0c;找到根本原因。 # 错误1 Tnauthorized: fa…