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

该程序改良自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 simpledialogdef select_file_path(): #选择视频路径root = tk.Tk()root.withdraw()  # 隐藏主窗口video_path = filedialog.askopenfilename()  # 选择文件if video_path:print(f"Selected file path: {video_path}")return video_pathelse:print("No file selected.")def get_user_input(): # 线程数,建议CPU线程数-1root = tk.Tk()root.withdraw()  # 隐藏主窗口thread_num = simpledialog.askinteger("输入线程", "请输入一个整数作为线程数:(建议设置为CPU线程数-1)")if thread_num is not None:print(f"User input: {thread_num}")return thread_numelse:print("No input provided.")
video_path = select_file_path()
thread_num = get_user_input()
out_path = "VideoTestOut/"  # 输出目录
huaZhi = 1  # 清晰度,最低1,无上限# -----以下为程序使用变量-----#
video_info = []num = 0info = []
'''
图片转换成字符里面的相关大小,
为元组,第一个是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 * 8index = 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:breakdef mergeImage():print("开始将图片合并成MP4视频")# global numvideoWriter = 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 numfor 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.rhkb.cn/news/399714.html

如若内容造成侵权/违法违规/事实不符,请联系长河编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

20240813作业

#include<head.h> #define SER_PORT 8888 //与服务器保持一致 #define SER_IP "192.168.106.1" //服务器ip地址 #define CLI_PORT 6666 //客户端端口号 #define CLI_IP "192.168.106.128" //客户端ip地址 int …

CI/CD——CI持续集成实验

目录 一. 安装Docker 二. 部署Jenkins 三. 配置邮箱 四. Harbor部署 五. Nexus Repository部署 五. sonarqube安装 六. 配置Docker 七. jenkins系统配置sonarqube 八. 配置pipeline 九. 构建并集成 一. 安装Docker docker-ce镜像_docker-ce下载地址_docker-ce安装教程…

Java入门基础16:集合框架1(Collection集合体系、List、Set)

集合体系结构 Collection是单列集合的祖宗&#xff0c;它规定的方法&#xff08;功能&#xff09;是全部单列集合都会继承的。 collection集合体系 Collection的常用方法 package com.itchinajie.d1_collection;import java.util.ArrayList; import java.util.HashSet;/* * 目…

靶机:DC-4

一、信息收集 1、主机发现 nmap 192.168.236.0/24 2、端口扫描 nmap 192.168.236.175 -p- -A 3、目录扫描 dirb http://192.168.236.175 二、漏洞探测 访问80端口&#xff0c;发现登录页面 尝试爆破密码 hydra -l admin -P /usr/share/wordlists/rockyou.txt 192.168.236…

修改docker的/var/lib/docker/overlay2储存路径

目录 目录 1.准备新的存储位置 1.创建新的存储目录 2.修改目录权限 2. 配置 Docker 使用新的存储位置 1.停止 Docker 服务 2.编辑 Docker 配置文件 3.迁移现有 Docker 数据 1.将现有的 Docker 数据从系统盘移动到新目录 2.启动 Docker 服务 3. 验证更改 4. 清理旧的…

Vue 3+Vite+Eectron从入门到实战系列之(五)一后台管理登录页

前面已经讲了不少基础知识&#xff0c;这篇开始&#xff0c;我们进行实操&#xff0c;做个后台管理系统&#xff0c;打包成多端的,可安装的桌面app!!其中&#xff0c;登录&#xff0c;退出的提示信息用系统的提示&#xff0c;不使用elemengplus的弹窗提示&#xff01;&#xff…

基于料面视频图像分析的高炉异常状态智能感知与识别

源自&#xff1a;自动化学报 作者&#xff1a;朱霁霖 桂卫华 蒋朝辉 陈致蓬 方怡静 注&#xff1a;若出现无法显示完全的情况&#xff0c;可 V 搜索“人工智能技术与咨询”查看完整文章 人工智能、大数据、多模态大模型、计算机视觉、自然语言处理、数字孪生、深度强化学习 …

shuishusihui

互斥量 使用互斥量可以用于保护某些临界资源&#xff0c;同一时间只能有一个任务来使用它。 使用互斥量会引入其他问题&#xff0c;比如说优先级反转&#xff0c;于是提出了优先级继承等方法解决问题 任务通知 任务通知就是通知任务&#xff0c;前边都是多对多的关系&#xff0…

从零开始构建基于ChatGPT的嵌入式(Embedding)本地医疗客服问答机器人模型(看完就会,看到最后有惊喜)

1、前言 代码全部开源,GitHub地址为: github.com/aehyok/go-o… 前端完全也能搭建&#xff0c; 前端完全也能搭建&#xff0c; 前端完全也能搭建&#xff0c; 本文中我使用的是后端语言golang,来调用的所有外部接口&#xff0c;但它们均是restful api,所以如果你使用的是其他…

此处不允许使用 ‘空‘ 类型

说明&#xff1a;受最近看的书《设计模式之美》&#xff08;小争哥&#xff09;的影响&#xff0c;最近编码有意将一些业务逻辑写在对象里面&#xff0c;增强封装性。在此记录一次项目启动时的报错&#xff0c;如下&#xff1a; 原因&#xff1a;当你在实体类对象中&#xff0c…

提升医疗器械维修技术必经的几个阶段

01 懵懂入门阶段 初入医疗器械维修领域&#xff0c;就如同踏入了一个充满未知的神秘世界。此时&#xff0c;菜鸟们对各种医疗器械的了解仅限于书本知识和简单的操作培训。他们可能刚刚熟悉了一些基本工具的使用方法&#xff0c;对常见的医疗器械类型和品牌有了初步的认识&#…

java设计模式-桥接模式

一. 概述 桥接模式&#xff08;Bridge Pattern&#xff09;是一种结构型设计模式&#xff0c;用于将抽象部分与其实现部分分离&#xff0c;使它们可以独立地变化。桥接模式主要目的是解决当一个类存在多个继承等级时&#xff0c;由于继承带来的耦合问题&#xff0c;以及扩展性不…

计算机网络408考研 2015

计算机网络408考研2015年真题解析_哔哩哔哩_bilibili 1 1线路编码(NRZ,NRZI,8B/10B,Manchester)与加扰_nrz编码-CSDN博客 1 1 11

19c做好这件事,大幅提升Data Pump工作效率

老司机遇到的新问题 expdp是Oracle 10g引入的数据导出工具&#xff0c;能够提供并行、压缩及元数据导出等更多的功能&#xff0c;在后续的版本中逐渐替代了传统的数据导出工具exp&#xff0c;是数据库开发运维常用的工具之一。在我的印象中&#xff0c;这个工具除了诸如大量的…

河北移动:核心系统数据库成功完成整体迁移 ,实现全栈国产|OceanBase案例

本文作者&#xff1a;移动通信集团河北有限公司架构规划专家&#xff0c;房瑞 项目背景&#xff1a; 中国移动通信集团河北有限公司一直在积极响应国家及集团的号召&#xff0c;以磐舟&磐基云原生为底座&#xff0c;结合国产浏览器、中间件、数据库、操作系统和服务器等&a…

Jupyter Notebook介绍、安装及使用教程

文章目录 一.什么是Jupyter Notebook&#xff1f;1.Jupyter Notebok简介2.组成部分3.Jupyter Notebook的主要特点 二.安装Jupyter Notebook0.先试用再决定1.安装①安装前提②使用Anaconda安装③使用pip命令安装 三.运行Jupyter Notebook0.帮助1.启动①默认端口启动②指定端口启…

零基础学会机器学习,到底要多久?

这两天啊&#xff0c;有不少朋友和我说&#xff0c;想学机器学习&#xff0c;但是之前没有基础&#xff0c;不知道能不能学得会。 首先说结论&#xff0c;只要坚持&#xff0c;就能学会&#xff0c;但是一定不能三天打鱼两天晒网&#xff0c;要持之以恒&#xff0c;至少每隔两…

自由职业四年,我整理了一些建议

我是勋荣&#xff0c;一个独立开发者。运营了自己的社群&#xff0c;有自己的软件产品。目前还在探索各种副业的路上~ 1我的独立开发之路 刚毕业就找不到Android岗位的我瑟瑟发抖。在广州&#xff0c;稀里糊涂做了Java后端开发。有一天加班 通宵&#xff0c;早上借住在同事家…

Python教程(十三):常用内置模块详解

目录 专栏列表1. os 模块2. sys 模块3. re 模块4. json 模块5. datetime 模块6. math 模块7. random 模块8. collections 模块9. itertools 模块10. threading 模块11. 加密 模块 总结 专栏列表 Python教程&#xff08;十&#xff09;&#xff1a;面向对象编程&#xff08;OOP…

【MySQL】什么是索引?了解索引的底层原理

索引的概念 索引是一种用于提高数据库查询效率的数据结构。它类似于书籍的目录&#xff0c;通过快速定位数据的方式&#xff0c;减少了数据检索的时间。索引在数据库表中可以被看作是一个指向数据的指针&#xff0c;它们存储了列的值及其对应行的位置&#xff0c;从而使得数据…