字节律动之*你太美, emm 其实是个字符画雪花视频-哈哈哈-将视频转成一张张字符画图片

效果

整体效果
在这里插入图片描述
局部图片放大效果
在这里插入图片描述
视频转换后带雪花特效,凑合看吧, 视频地址

准备工作

安装FFmpeg

电脑上安装ffpeg处理视频并设置环境变量, windows可以参考FFmpeg的安装教程这篇博客安装
mac可以直接执行brew install ffmpeg安装

安装python依赖包

执行pip3 install -r requirements.txt 安装依赖包
requirements.txt 内容如下

attrs==22.1.0
certifi==2022.9.24
charset-normalizer==2.1.1
decorator==4.4.2
exceptiongroup==1.0.4
ffmpeg==1.4
idna==3.4
imageio==2.22.4
imageio-ffmpeg==0.4.7
iniconfig==1.1.1
moviepy==1.0.3
numpy==1.23.5
opencv-contrib-python==4.6.0.66
opencv-python==4.6.0.66
packaging==21.3
Pillow==9.3.0
pluggy==1.0.0
proglog==0.1.10
pyparsing==3.0.9
pytest==7.2.0
python3-tk==0.0.0
requests==2.28.1
tomli==2.0.1
tqdm==4.64.1
urllib3==1.26.12

代码

video.py完整代码如下:

import os, shutil, cv2, random
import datetime
import geventimport numpy as np
from PIL import Image, ImageFont, ImageDraw
from tkinter import Tk, filedialog
from moviepy.audio.io.AudioFileClip import AudioFileClip
from moviepy.video.io.VideoFileClip import VideoFileClipfps = int()
frames = int()# 获取视频路径
def v_path():root = Tk()root.withdraw()file_path = (filedialog.askopenfilename(title="选择视频文件", filetypes=[('mp4', '*.mp4'), ('All Files', '*')]))return file_path# 将视频分解为帧和音轨
def split_frames(v_path):# exist_ok表示目录存在时不必报错, 替代os.mkdir()os.makedirs("images", exist_ok=True)os.makedirs("music", exist_ok=True)music = AudioFileClip(v_path)music.write_audiofile("music/origin.wav")# 视频v1 = cv2.VideoCapture()v1.open(v_path)global fps, frames, ifps = v1.get(cv2.CAP_PROP_FPS)frames = v1.get(cv2.CAP_PROP_FRAME_COUNT)for i in range(int(frames)):ret, frame = v1.read()cv2.imwrite("images/%d.png" % i, frame)return# 获取保存路径
def save_path():root = Tk()root.withdraw()file_path = (filedialog.asksaveasfilename(title="保存视频文件", filetypes=[('mp4', '*.mp4'), ('All Files', '*')]))return file_path#  方式1: 通过什么鸢大佬的代码将每帧图片转换为彩色字符图片, 生成图片只能看清人物轮廓比较模糊
def frames2text():os.makedirs("text", exist_ok=True)for n in range(int(frames)):IMG = ("images/%d.png" % n)# 字符列表,可自定义文字种类和数量ascii_char = list('1010')def get_char(r, g, b, alpha=256):# alpha透明度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)]im = Image.open(IMG)# 宽度调整为原图1/6,由于字体宽度WIDTH = int(im.width / 6)# 高度调整为原图1/15,由于字体宽度HEIGHT = int(im.height / 15)# 黑色背景图片im_text = Image.new("RGB", (im.width, im.height), (255, 255, 255))# Image.NEAREST(最近邻采样),选择输入图像的最近像素;忽略其它输入像素.im = im.resize((WIDTH, HEIGHT), Image.NEAREST)txt = ""colors = []for i in range(HEIGHT):for j in range(WIDTH):pixel = im.getpixel((j, i))# 记录像素颜色信息colors.append((pixel[0], pixel[1], pixel[2]))if (len(pixel) == 4):txt += get_char(pixel[0], pixel[1], pixel[2], pixel[3])else:txt += get_char(pixel[0], pixel[1], pixel[2])txt += '\n'colors.append((255, 255, 255))# 开始画图dr = ImageDraw.Draw(im_text)# 获取默认字体font = ImageFont.load_default()x = y = 0# 获取字体宽高font_w, font_h = font.getsize(txt[1])# 调整比例font_h *= 1.37# 为每个ascii码字符上色for i in range(len(txt)):# 对换行符特殊处理,不上色并且保证位置正确if (txt[i] == '\n'):x += font_hy = -font_wdr.text([y, x], txt[i], colors[i])y += font_w# 保存生成的每帧彩色字符图片save_path = ("text/%d" % n)save_path = save_path + '.png'im_text.save(save_path)# 删除 images目录# shutil.rmtree("images")# 方式2: 通过修改盗蓝大佬的代码生成高清版本彩色图片, 每张图片大小变为3*3倍
def img2clorfultxt(frame, K=5):"""利用 聚类 将像素信息聚为3或5类,颜色最深的一类用数字密集地表示,阴影的一类用“*”表示,明亮部分空白或者"-"横杠表示。---------------------------------frame:需要传入的图片信息。可以是opencv的cv2.imread()得到的数组,也可以是Pillow的Image.read()。K:聚类数量,推荐的K为3或5。根据经验,3或5时可以较为优秀地处理很多图像了。若默认的K=5无法很好地表现原图,请修改为3进行尝试。若依然无法很好地表现原图,请换图尝试。 ( -_-|| )---------------------------------聚类数目理论可以取大于等于3的任意整数。但水平有限,无法自动判断当生成的字符画可以更好地表现原图细节时,“黑暗”、“阴影”、”明亮“之间边界在哪。所以说由于无法有效利用更大的聚类数量,那么便先简单地限制聚类数目为3和5。"""if type(frame) != np.ndarray:frame = np.array(frame)height, width, *_ = frame.shape  # 有时返回两个值,有时三个值frame_gray = cv2.cvtColor(frame, cv2.COLOR_BGR2GRAY)frame_array = np.float32(frame_gray.reshape(-1))# 设置相关参数。criteria: 这是迭代终止准则。当满足这个准则时,算法迭代停止criteria = (cv2.TERM_CRITERIA_EPS + cv2.TERM_CRITERIA_MAX_ITER, 10, 1.0)flags = cv2.KMEANS_RANDOM_CENTERS# 得到labels(类别)、centroids(矩心)。# 如第一行6个像素labels=[0,2,2,1,2,0],则意味着6个像素分别对应着 第1个矩心、第3个矩心、第3、2、3、1个矩心。compactness, labels, centroids = cv2.kmeans(frame_array, K, None, criteria, 10, flags)centroids = np.uint8(centroids)# labels的数个矩心以随机顺序排列,所以需要简单处理矩心.centroids = centroids.flatten()centroids_sorted = sorted(centroids)# 获得不同centroids的明暗程度,0最暗centroids_index = np.array([centroids_sorted.index(value) for value in centroids])bright = [abs((3 * i - 2 * K) / (3 * K)) for i in range(1, 1 + K)]bright_bound = bright.index(np.min(bright))shadow = [abs((3 * i - K) / (3 * K)) for i in range(1, 1 + K)]shadow_bound = shadow.index(np.min(shadow))labels = labels.flatten()# 将labels转变为实际的明暗程度列表,0最暗。labels = centroids_index[labels]# 列表解析,每2*2个像素挑选出一个,组成(height*width*灰)三维数组。labels_picked = [labels[rows * width:(rows + 1) * width:2] for rows in range(0, height, 2)]canvas = np.zeros((3 * height, 3 * width, 3), np.uint8)canvas.fill(255)  # 创建长宽为原图三倍的白色画布。# 因为 字体大小为0.45时,每个数字占6*6个像素,而白底画布为原图三倍# 所以 需要原图中每2*2个像素中挑取一个,在白底画布中由6*6像素大小的数字表示这个像素信息。cv2.FONT_HERSHEY_PLAIN为字体y = 8for rows in labels_picked:x = 0for cols in rows:if cols <= shadow_bound:# cv2.putText各参数依次是:图片,添加的文字,左上角坐标,字体,字体大小,颜色,字体粗细# (x,y)坐标颜色对应frame[((y // 3) - 1), ((x // 3) - 1)], 各个颜色值必须迭代强转成int组成的元组例如 (147, 132, 132)#  迭代转成元组的方法, tuple([int(c) for c in frame[((y // 3) - 1), ((x // 3) - 1)]]),#  不强制转换会报错 - Scalar value for argument 'color' is not numericcv2.putText(canvas, str(random.randint(2, 9)),(x, y), cv2.FONT_HERSHEY_PLAIN, 0.45,tuple([int(c) for c in frame[((y // 3) - 1), ((x // 3) - 1)]]), 1)elif cols <= bright_bound:cv2.putText(canvas, "*", (x, y),cv2.FONT_HERSHEY_PLAIN, 0.4, tuple([int(c) for c in frame[((y // 3) - 1), ((x // 3) - 1)]]),1)else:cv2.putText(canvas, "-", (x, y),cv2.FONT_HERSHEY_PLAIN, 0.4, tuple([int(c) for c in frame[((y // 3) - 1), ((x // 3) - 1)]]),1)x += 6y += 6return canvas# 用于保存第n张字符图片
def save_nth_strframe(n):# 读取每张图片fp = ("images/%d.png" % n)img = cv2.imread(fp)# 生成彩色字符画图片并保存str_img = img2clorfultxt(img)save_path = ("text/%d.png" % n)cv2.imwrite(save_path, str_img)# 保存所有字符画图片: 方式1. 通过for循环将每帧图片转成字符画图片, 速度较慢可以通过多线程和多进程加速
def frames2hdtext():os.makedirs("text", exist_ok=True)for n in range(int(frames)):# 保存彩色字符图片到text路径save_nth_strframe(n)# 删除 images目录# shutil.rmtree("images")# 保存所有字符画图片: 方式2. 通过多进程+多线程结合的方式加速
# 参考https://blog.csdn.net/qq_35869630/article/details/106026450
def trans_hdtext_thread(split_array):import concurrent.futureswith concurrent.futures.ThreadPoolExecutor(max_workers=10) as executor:future_list = {executor.submit(save_nth_strframe, i): i for i in split_array}# 传入 Future 列表,在每个 Future 完成时产出结果,此方法不阻塞。concurrent.futures.as_completed(future_list)# 保存所有字符画图片: 方式3. 通过多进程+协程结合的方式加速
def trans_hdtext_coroutine(*params):'''创建协程'''spawn_list = []for array in params:for n in array:spawn = gevent.spawn(save_nth_strframe, n)spawn_list.append(spawn)# gevent.joinall 会阻塞当前流程,并执行所有给定的 greenlet,执行流程只会在所有 greenlet 执行完后才会继续向下走。 # https://blog.csdn.net/freeking101/article/details/53097420gevent.joinall(spawn_list)def trans_hdtext_process_pool():os.makedirs("text", exist_ok=True)from multiprocessing import Pool, cpu_countNUMBER_OF_PROCESSES = cpu_count()pool = Pool(NUMBER_OF_PROCESSES - 1)# 将0到frames的数组拆分成NUMBER_OF_PROCESSES-1份子数组, 启动NUMBER_OF_PROCESSES-1个进程frames_array = np.arange(int(frames))# 多线程方式, 例如[1:9]的列表拆成三个子数组就变成了[ array([1,2,3]), array([4,5,6]), array([7,8,9]) ]# pool.map(trans_hdtext_thread, np.array_split(frames_array, NUMBER_OF_PROCESSES - 1))# 协程方式pool.map(trans_hdtext_coroutine, np.array_split(frames_array, NUMBER_OF_PROCESSES - 1))pool.close()pool.join()# 帧合成新视频
def images2video():img_array = []# 按照第一张图片大小初始化img = cv2.imread("text/0.png")sp = img.shapeimg_width = sp[1]img_height = sp[0]for i in range(int(frames)):file_path = "text/%d" % ifilename = file_path + ".png"img = cv2.imread(filename)if img is None:print(filename + "不存在")continueimg_array.append(img)# 合成视频, fourcc 指定编码器,fps 要保存的视频的帧率out = cv2.VideoWriter("music/origin.mp4", cv2.VideoWriter_fourcc(*'mp4v'), fps, (img_width, img_height))for i in range(len(img_array)):out.write(img_array[i])out.release()# 删除 text目录# shutil.rmtree("text")# 合成视频和音轨
def merge_video_with_audio(save_path):# 原始视频origin_video = "music/origin.mp4"audio = "music/origin.wav"video_clip = VideoFileClip(origin_video)audio_clip = AudioFileClip(audio)# 输出到新视频video = video_clip.set_audio(audio_clip)# audio_codec='aac'改变下声道编码,避免mac播放器播放时没有声音video.write_videofile(save_path, audio_codec='aac')video_clip.close()audio_clip.close()if __name__ == '__main__':# 原始视频路径video_path = v_path()# 保存路径save_video = save_path()starttime = datetime.datetime.now()split_frames(video_path)endtime = datetime.datetime.now()print("split_frames: ", starttime, endtime, (endtime - starttime).seconds)#  方式1: 通过什么鸢大佬的代码将每帧图片转换为彩色字符图片, 生成图片只能看清人物轮廓比较模糊# frames2text()#  方式2: 通过修改盗蓝大佬的代码生成高清版本彩色图片, 每张图片大小变为3*3倍,  通过for循环将每帧图片转成字符画图片,速度较慢 可以通过多线程加速# frames2hdtext()# 方式3: 通过多进程+多线程方式加速生成高清图片starttime = datetime.datetime.now()trans_hdtext_process_pool()endtime = datetime.datetime.now()print("trans_hdtext_process_pool: ", starttime, endtime, (endtime - starttime).seconds)starttime = datetime.datetime.now()images2video()endtime = datetime.datetime.now()print("images2video: ", starttime, endtime, (endtime - starttime).seconds)merge_video_with_audio(save_video)# # 删除music目录# shutil.rmtree("music")

测试代码可用于单张图片转换成字符画图片
test.py代码如下

from PIL import Image, ImageFont, ImageDrawimport cv2
import random
import numpy as np
import pytest# 盗蓝大佬的高清图像生成 https://zhuanlan.zhihu.com/p/56033037
# OpenCV-Python教程:55.OpenCV里的K-Means聚类   https://www.jianshu.com/p/b24086aab3fc
# KMeans聚类算法详解 https://zhuanlan.zhihu.com/p/184686598
def img2strimg(frame, K=5):"""利用 聚类 将像素信息聚为3或5类,颜色最深的一类用数字密集地表示,阴影的一类用“-”横杠表示,明亮部分空白表示。---------------------------------frame:需要传入的图片信息。可以是opencv的cv2.imread()得到的数组,也可以是Pillow的Image.read()。K:聚类数量,推荐的K为3或5。根据经验,3或5时可以较为优秀地处理很多图像了。若默认的K=5无法很好地表现原图,请修改为3进行尝试。若依然无法很好地表现原图,请换图尝试。 ( -_-|| )---------------------------------聚类数目理论可以取大于等于3的任意整数。但水平有限,无法自动判断当生成的字符画可以更好地表现原图细节时,“黑暗”、“阴影”、”明亮“之间边界在哪。所以说由于无法有效利用更大的聚类数量,那么便先简单地限制聚类数目为3和5。"""if type(frame) != np.ndarray:frame = np.array(frame)height, width, *_ = frame.shape  # 有时返回两个值,有时三个值frame_gray = cv2.cvtColor(frame, cv2.COLOR_BGR2GRAY)frame_array = np.float32(frame_gray.reshape(-1))# 设置相关参数。criteria = (cv2.TERM_CRITERIA_EPS + cv2.TERM_CRITERIA_MAX_ITER, 10, 1.0)flags = cv2.KMEANS_RANDOM_CENTERS# 得到labels(类别)、centroids(矩心)。# 如第一行6个像素labels=[0,2,2,1,2,0],则意味着6个像素分别对应着 第1个矩心、第3个矩心、第3、2、3、1个矩心。compactness, labels, centroids = cv2.kmeans(frame_array, K, None, criteria, 10, flags)centroids = np.uint8(centroids)# labels的数个矩心以随机顺序排列,所以需要简单处理矩心.centroids = centroids.flatten()centroids_sorted = sorted(centroids)# 获得不同centroids的明暗程度,0最暗centroids_index = np.array([centroids_sorted.index(value) for value in centroids])bright = [abs((3 * i - 2 * K) / (3 * K)) for i in range(1, 1 + K)]bright_bound = bright.index(np.min(bright))shadow = [abs((3 * i - K) / (3 * K)) for i in range(1, 1 + K)]shadow_bound = shadow.index(np.min(shadow))labels = labels.flatten()# 将labels转变为实际的明暗程度列表,0最暗。labels = centroids_index[labels]# 列表解析,每2*2个像素挑选出一个,组成(height*width*灰)数组。labels_picked = [labels[rows * width:(rows + 1) * width:2] for rows in range(0, height, 2)]canvas = np.zeros((3 * height, 3 * width, 3), np.uint8)canvas.fill(255)  # 创建长宽为原图三倍的白色画布。# 因为 字体大小为0.45时,每个数字占6*6个像素,而白底画布为原图三倍# 所以 需要原图中每2*2个像素中挑取一个,在白底画布中由6*6像素大小的数字表示这个像素信息。y = 8for rows in labels_picked:x = 0for cols in rows:if cols <= shadow_bound:cv2.putText(canvas, str(random.randint(2, 9)),(x, y), cv2.FONT_HERSHEY_PLAIN, 0.45, 1)elif cols <= bright_bound:cv2.putText(canvas, "-", (x, y),cv2.FONT_HERSHEY_PLAIN, 0.4, 0, 1)x += 6y += 6return canvas# 根据盗蓝大佬的代码修改生成彩色高清字符画图片
def img2clorfultxt(frame, K=5):"""利用 聚类 将像素信息聚为3或5类,颜色最深的一类用数字密集地表示,阴影的一类用“*”表示,明亮部分空白或者"-"横杠表示。---------------------------------frame:需要传入的图片信息。可以是opencv的cv2.imread()得到的数组,也可以是Pillow的Image.read()。K:聚类数量,推荐的K为3或5。根据经验,3或5时可以较为优秀地处理很多图像了。若默认的K=5无法很好地表现原图,请修改为3进行尝试。若依然无法很好地表现原图,请换图尝试。 ( -_-|| )---------------------------------聚类数目理论可以取大于等于3的任意整数。但水平有限,无法自动判断当生成的字符画可以更好地表现原图细节时,“黑暗”、“阴影”、”明亮“之间边界在哪。所以说由于无法有效利用更大的聚类数量,那么便先简单地限制聚类数目为3和5。"""if type(frame) != np.ndarray:frame = np.array(frame)height, width, *_ = frame.shape  # 有时返回两个值,有时三个值frame_gray = cv2.cvtColor(frame, cv2.COLOR_BGR2GRAY)frame_array = np.float32(frame_gray.reshape(-1))# 设置相关参数。criteria: 这是迭代终止准则。当满足这个准则时,算法迭代停止criteria = (cv2.TERM_CRITERIA_EPS + cv2.TERM_CRITERIA_MAX_ITER, 10, 1.0)flags = cv2.KMEANS_RANDOM_CENTERS# 得到labels(类别)、centroids(矩心)。# 如第一行6个像素labels=[0,2,2,1,2,0],则意味着6个像素分别对应着 第1个矩心、第3个矩心、第3、2、3、1个矩心。compactness, labels, centroids = cv2.kmeans(frame_array, K, None, criteria, 10, flags)centroids = np.uint8(centroids)# labels的数个矩心以随机顺序排列,所以需要简单处理矩心.centroids = centroids.flatten()centroids_sorted = sorted(centroids)# 获得不同centroids的明暗程度,0最暗centroids_index = np.array([centroids_sorted.index(value) for value in centroids])bright = [abs((3 * i - 2 * K) / (3 * K)) for i in range(1, 1 + K)]bright_bound = bright.index(np.min(bright))shadow = [abs((3 * i - K) / (3 * K)) for i in range(1, 1 + K)]shadow_bound = shadow.index(np.min(shadow))labels = labels.flatten()# 将labels转变为实际的明暗程度列表,0最暗。labels = centroids_index[labels]# 列表解析,每2*2个像素挑选出一个,组成(height*width*灰)三维数组。labels_picked = [labels[rows * width:(rows + 1) * width:2] for rows in range(0, height, 2)]canvas = np.zeros((3 * height, 3 * width, 3), np.uint8)canvas.fill(255)  # 创建长宽为原图三倍的白色画布。# 因为 字体大小为0.45时,每个数字占6*6个像素,而白底画布为原图三倍# 所以 需要原图中每2*2个像素中挑取一个,在白底画布中由6*6像素大小的数字表示这个像素信息。cv2.FONT_HERSHEY_PLAIN为字体y = 8for rows in labels_picked:x = 0for cols in rows:if cols <= shadow_bound:# cv2.putText各参数依次是:图片,添加的文字,左上角坐标,字体,字体大小,颜色,字体粗细# (x,y)坐标颜色对应frame[((y // 3) - 1), ((x // 3) - 1)], 各个颜色值必须迭代强转成int组成的元组例如 (147, 132, 132)#  迭代转成元组的方法, tuple([int(c) for c in frame[((y // 3) - 1), ((x // 3) - 1)]]),#  不强制转换会报错 - Scalar value for argument 'color' is not numericcv2.putText(canvas, str(random.randint(2, 9)),(x, y), cv2.FONT_HERSHEY_PLAIN, 0.45,tuple([int(c) for c in frame[((y // 3) - 1), ((x // 3) - 1)]]), 1)elif cols <= bright_bound:cv2.putText(canvas, "*", (x, y),cv2.FONT_HERSHEY_PLAIN, 0.4, tuple([int(c) for c in frame[((y // 3) - 1), ((x // 3) - 1)]]),1)else:cv2.putText(canvas, "-", (x, y),cv2.FONT_HERSHEY_PLAIN, 0.4, tuple([int(c) for c in frame[((y // 3) - 1), ((x // 3) - 1)]]),1)x += 6y += 6return canvas# 什么鸢大佬的根据灰度重新画字符画图片, 该方法只能显示轮廓和颜色,人物比较模糊
# https://www.bilibili.com/video/BV1sQ4y1Y7P2/
def frame2text(png, save_path):IMG = png# 字符列表,可自定义文字种类和数量ascii_char = list('1010')def get_char(r, g, b, alpha=256):# alpha透明度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)]im = Image.open(IMG)# 宽度调整为原图1/6,由于字体宽度WIDTH = int(im.width / 6)# 高度调整为原图1/15,由于字体宽度HEIGHT = int(im.height / 15)# 黑色背景图片im_text = Image.new("RGB", (im.width, im.height), (255, 255, 255))# Image.NEAREST(最近邻采样),选择输入图像的最近像素;忽略其它输入像素.im = im.resize((WIDTH, HEIGHT), Image.NEAREST)txt = ""colors = []for i in range(HEIGHT):for j in range(WIDTH):pixel = im.getpixel((j, i))# 记录像素颜色信息colors.append((pixel[0], pixel[1], pixel[2]))if (len(pixel) == 4):txt += get_char(pixel[0], pixel[1], pixel[2], pixel[3])else:txt += get_char(pixel[0], pixel[1], pixel[2])txt += '\n'colors.append((255, 255, 255))# 开始画图dr = ImageDraw.Draw(im_text)# 获取默认字体font = ImageFont.load_default()x = y = 0# 获取字体宽高font_w, font_h = font.getsize(txt[1])# 调整比例font_h *= 1.37# 为每个ascii码字符上色for i in range(len(txt)):# 对换行符特殊处理,不上色并且保证位置正确if (txt[i] == '\n'):x += font_hy = -font_wdr.text([y, x], txt[i], colors[i])y += font_w# 保存生成的每帧彩色字符图im_text.save(save_path)# 测试代码部分def test_img2strimg():fp1 = "test/59.png"fp2 = "test/97.png"img = cv2.imread(fp1)img2 = cv2.imread(fp2)# 若字符画结果不好,可以尝试更改K为3。若依然无法很好地表现原图,请换图尝试。 -_-||str_img1 = img2strimg(img)str_img2 = img2strimg(img2)cv2.imwrite("test/59-img2strimg.png", str_img1)cv2.imwrite("test/97-img2strimg.png", str_img2)def test_img2clorfultxt():fp1 = "test/59.png"fp2 = "test/97.png"img = cv2.imread(fp1)img2 = cv2.imread(fp2)str_img = img2clorfultxt(img)str_img2 = img2clorfultxt(img2)cv2.imwrite("test/59-img2clorfultxt.png", str_img)cv2.imwrite("test/97-img2clorfultxt.png", str_img2)def test_frame2text():png1 = "test/59.png"png2 = "test/97.png"save_path1 = "test/59-frame2text.png"save_path2 = "test/97-frame2text.png"frame2text(png1, save_path1)frame2text(png2, save_path2)if __name__ == '__main__':print(np.array_split(1417, 3))

项目地址: https://github.com/keginx/logorhythums

项目结构

.
├── README.md
├── images            视频每一帧转换成的图片
│   ├── 0.png
│   └── 999.png
├── music             原始视频拆分成mp4视频和声音
│   ├── origin.mp4
│   └── origin.wav
├── requirements.txt  依赖
├── test              各种方法实现的字符画图片转换效果
│   ├── 230.png
│   ├── 59-frame2text.png
│   ├── 59-img2clorfultxt.png
│   ├── 59-img2strimg.png
│   ├── 59.png
│   ├── 97-frame2text.png
│   ├── 97-img2clorfultxt.png
│   ├── 97-img2strimg.png
│   └── 97.png
├── test.py         测试各种方法转字符画图片
├── test.txt
├── text            转换后的每帧字符画图片
│   ├── 0.png
│   └── 999.png
└── video.py        实现字符画视频转换

其他

遇到的一些问题

  1. 弹窗功能库tkinter调用失败:import _tkinter # If this fails your Python may not be configured for Tk、ModuleNotF
    解决办法: 执行brew install python-tk@3.10 安装
  2. mac MoviePy 合成视频没有声音
    解决办法: 在调用write_videofile函数时加上参数audio_codec=“aac”,改变下声道编码即可
  3. Scalar value for argument ‘color‘ is not numeric错误
    解决方法: 用迭代的方式 转成需要的类型
     color = tuple ([int(x) for x in color])  #设置为整数
    

一些优化方向

  1. Sklearnex加速KMeans计算
    sklearnex是一个sklearn加速包,大约可以提升几倍到千倍的速度。它是针对Intel平台的,只对Intel CPU有效,且CPU性能越好加速效果越显著
  2. 使用GPU
    有很多GPU版本的KMeans算法包,大多数基于cuda或者torch、tensorflow的。
  3. 代码中用的字符是1234567890,可以尝试用其他字符代替

一些建议

被转换视频建议选择一些小的视频不然会很耗时间

参考

  1. FFmpeg的安装教程
  2. Python小程序:视频转彩色字符动画
  3. 弹窗功能库tkinter调用失败:import _tkinter # If this fails your Python may not be configured for Tk、ModuleNotF
  4. mac MoviePy 合成视频没有声音
  5. python 多进程编程 之 加速kmeans算法
  6. Scikit-Learn学习笔记——k-means聚类:图像识别、色彩压缩
  7. python KMeans聚类加速计算一些简单技巧和坑总结
  8. 如何将Numpy加速700倍?用 CuPy 呀
  9. Python 多线程+多进程简单使用教程,如何在多进程开多线程

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

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

相关文章

.NET 操作 TDengine .NET ORM

TDengine 是国内比较流的时序库之一&#xff0c;支持群集并且免费&#xff0c;在.NET中资料比较少&#xff0c;这篇文章主要介绍SqlSugar ORM来操作TDengine 优点&#xff1a; 1、SqlSugar支持ADO.NET操作来实现TDengine&#xff0c;并且支持了常用的时间函数、支持联表、分…

如何注册一个免费的网站空间和域名

https://www.jianshu.com/p/016c83f70d43 前几天想搞个网站玩玩&#xff0c;于是就上网搜哪里可以注册免费的网站空间和域名&#xff0c;折腾了好几天都没注册成&#xff0c;国内好像已经没有免费的了。只好用英文去搜国外的网站&#xff0c;才找到了一个能注册成功的&#xff…

MybatisPlus 项目中使用

大家好 , 我是苏麟 , 今天带来 MybatisPlus 的简单使用 . 官方网站 : MyBatis-Plus (baomidou.com) 开始使用 初步体验 引入依赖 <dependency><groupId>com.baomidou</groupId><artifactId>mybatis-plus-boot-starter</artifactId><version…

探秘腾讯Android手机游戏平台之不安装游戏APK直接启动法

探秘腾讯Android手机游戏平台之不安装游戏APK直接启动法 2011年09月18日 ⁄ 移动开发 ⁄ 评论数 38 ⁄ 被围观 8,791 前言 相信这样一个问题&#xff0c;大家都不会陌生&#xff0c; “有什么的方法可以使Android的程序APK不用安装&#xff0c;而能够直接启动”。 发现最后的…

群玩小游戏sdk一键接入

群玩提供第三方用户互动内容服务&#xff0c;拥有“你画我猜”、“谁是卧底”、“豆腐王国”、“猜歌名” 等多款互动竞猜类游戏、智能机器人接入服务&#xff0c;适合直播、社交、社群行业&#xff0c;可以自定义设计玩法&#xff0c;页面精美有趣&#xff0c;接入方便快捷&am…

职业成长面对面交流会

职业成长面对面交流会 ——微软高级主管欧琼老师讲座2009年4月12日星期日上午十点半由电子工业出版社博文视点资讯有限公司隆重举办一场“职业成长面对面交流会”&#xff0c;主讲老师是《微软360度-成功和成长》主要作者 微软总部高级主管 欧琼老师&#xff0c;精彩的讲座在开…

游戏客户端面试(Unity)

推荐阅读&#xff1a; 我的CSDN 我的博客园 QQ群&#xff1a;704621321 我的个人博客 一。最开始的两家公司笔试面试题目 一家公司是学校聘请研究教育方面VR课件的公司&#xff0c;面试没几天&#xff0c;就收到了面试通过的消息&#xff0c;后面因为通过了另一家游戏公司而拒绝…

Unity - 微信小游戏

总参考&#xff1a;Unity WebGL 微信小游戏适配方案(公测) 安装与使用 下载 Unity插件&#xff0c;并导入至游戏项目中&#xff0c;版本更新请查看更新日志 请查阅推荐引擎版本&#xff0c;安装时选择WebGL组件 最终选择Unity2021.2.5f1c1InstantGame前往Node官网安装长期稳定…

聊天群内组局互动小游戏

“找我61”互动小游戏嵌入到聊天群内&#xff0c;会给社交应用&#xff08;产品&#xff09;增加不少人气&#xff0c;带来更多活力。 新颖社区社交产品“知多视广”架构构思设想

活动预告 | 与大神面对面,移动游戏开发者必须掌握的十大技巧

上半年&#xff0c;国内游戏下载量对比2019年下半年上涨将近50%&#xff0c;在用户旺盛的需求下&#xff0c;游戏行业迎来了绝佳的发展机遇。在市场火爆的背后&#xff0c;随着用户需求的多样化、时间的碎片化&#xff0c;一些因开发繁琐、资源不足的问题频频出现&#xff0c;让…

游戏大厅 从基础开始(1)——最简单的关系,用户与房间

游戏大厅 从基础开始&#xff08;1&#xff09; ——最简单的关系&#xff0c;用户与房间。 做游戏 就好像写一篇简单的记叙文 作为小学语文大纲要求 记叙文需要完整地叙述&#xff1a; 时间&#xff0c;地点&#xff0c;人物&#xff0c;事件 也就是 WHEN &#xff0c; WH…

RK3399平台开发系列讲解(存储篇)Linux 存储系统的 I/O 栈

平台内核版本安卓版本RK3399Linux4.4Android7.1🚀返回专栏总目录 文章目录 一、Linux 存储系统全景二、Linux 存储系统的缓存沉淀、分享、成长,让自己和他人都能有所收获!😄 📢本篇将介绍 Linux 存储系统的 I/O 原理。 一、Linux 存储系统全景 我们可以把 Linux 存储系…

解决MASM32代码汇编出错: error A2181: initializer must be a string or single item

最近用MASM32编程更新SysInfo&#xff0c;增加对IPv6连接信息的收集&#xff0c;使用到了 typedef struct _MIB_TCP6ROW_OWNER_MODULE {UCHAR ucLocalAddr[16];DWORD dwLocalScopeId;DWORD dwLocalPort;UCHAR ucRemoteAddr[16];DWORD …

文本分类任务

文章目录 引言1. 文本分类-使用场景2. 自定义类别任务3. 贝叶斯算法3.1 预备知识3.2 贝叶斯公式3.3 贝叶斯公式的应用3.4 贝叶斯公式在NLP中的应用3.5 贝叶斯公式-文本分类3.6 代码实现3.7 贝叶斯算法的优缺点 4. 支持向量机4.1 支持向量机-核函数4.2 支持向量机-解决多分类4.3…

android系统启动流程之zygote如何创建SystemServer进程

SystemServer:是独立的进程&#xff0c;主要工作是管理服务的&#xff0c;它将启动大约90种服务Services. 它主要承担的职责是为APP的运行提供各种服务&#xff0c;像AMS,WMS这些服务并不是一个独立的进程&#xff0c; 它们其实都是SystemServer进程中需要管理的的众多服务之一…

从零到富:探索CSGO搬砖项目的无限可能

在如今互联网时代&#xff0c;有一项令人惊叹的项目正悄然兴起&#xff0c;它就是CSGO搬砖项目。作为一个从零开始的家伙&#xff0c;我亲身经历了这个项目的神奇魅力&#xff0c;每天轻松赚取几十上百的收益&#xff0c;无风险&#xff0c;低成本。今天&#xff0c;我将带领大…

Redis—Redis介绍(是什么/为什么快/为什么做MySQL缓存等)

一、Redis是什么 Redis 是一种基于内存的数据库&#xff0c;对数据的读写操作都是在内存中完成&#xff0c;因此读写速度非常快&#xff0c;常用于缓存&#xff0c;消息队列、分布式锁等场景。 Redis 提供了多种数据类型来支持不同的业务场景&#xff0c;比如 String(字符串)、…

围棋与育种

最近因为娃子报名了围棋课&#xff0c;我本着交一次课学两个人的态度&#xff0c;入门围棋&#xff0c;买了些书&#xff0c;听了些课&#xff0c;有所得。学了两个多月&#xff0c;现在6级水平&#xff0c;了解了基本的攻杀技巧&#xff0c;会判断输赢。 下面截图是今天的一盘…

QQ号大规模被盗与你我有什么关系?你我该如何做?

QQ号大规模被盗与你我有什么关系&#xff1f;你我该如何做&#xff1f; 时值QQ号大规模被告事件已经过去多天&#xff0c;今天&#xff0c;我们再次回顾一下此次事件的经过&#xff1a; 1、大规模用户被盗号。 2、不法分子用盗得账号向家人、朋友及其他联系人发送不雅图片。 …

wireshark抓包QQ号

过滤栏输入oicq&#xff0c;然后随便打开一个数据包&#xff0c;就可看到其qq号。 查看移动端QQ的账号 因为是用的是TCP协议&#xff0c;而TCP数据包众多&#xff0c;所以不方便过滤。 但是我发现在info栏中带有scotty-ft字样的数据包中可以找到qq号。你可以按ctrlF选择字符串过…