字符视频转换器--保留中间的转换过程
# -*- coding: utf-8 -*- import os import threading import tkinter from tkinter import LEFT, RIGHT, filedialog, messagebox, DISABLED, NORMAL, TOP import cv2 from PIL import Image, ImageDraw, ImageFont ascii_char = list("$@B%8&WM#*oahkbdpqwmZO0QLCJUYXzcvunxrjft/\|()1{}[]?-_+~<>i!lI;:,\"^`'. ") # 所用字符列表 # 将256灰度映射到70个字符上 def get_char(r, g, b, alpha=256): 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 single_picture2char_img(origin_img_path, out_img_path): origin_img_width = 0 origin_img_height = 0 if os.path.exists(origin_img_path): im = Image.open(origin_img_path) origin_img_width = im.width origin_img_height = im.height new_img_width = int(im.width / 6) new_img_height = int(im.height / 15) im_txt = Image.new("RGB", (origin_img_width, origin_img_height), (255, 255, 255)) im = im.resize((new_img_width, new_img_height), Image.NEAREST) txt = "" colors = [] for i in range(new_img_height): for j in range(new_img_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_txt) font = ImageFont.load_default().font x = y = 0 font_w, font_h = font.getsize(txt[1]) font_h *= 1.37 for i in range(len(txt)): if txt[i] == '\n': x += font_h y = -font_w dr.text((y, x), txt[i], colors[i]) y += font_w im_txt.save(out_img_path) return origin_img_width, origin_img_height def picture2char_img(img_dir, char_img_dir): origin_img_width = 0 origin_img_height = 0 files = os.listdir(img_dir) for file in files: file_path = os.path.join(img_dir, file) if os.path.isfile(file_path): out_img_path = f"{char_img_dir}/{file}" origin_img_width, origin_img_height = single_picture2char_img(file_path, out_img_path) return origin_img_width, origin_img_height def char_img2video(char_img_dir, video_dir, origin_img_width, origin_img_height): fourcc = cv2.VideoWriter_fourcc('D', 'I', 'V', 'X') video_writer = cv2.VideoWriter(f"{video_dir}/output.mp4", fourcc, 30.0, (origin_img_width, origin_img_height)) files = sorted(os.listdir(char_img_dir), key=lambda x: int(x[:-4])) for file in files: filename = os.path.join(char_img_dir, file) if os.path.exists(filename): img = cv2.imread(filename=filename) cv2.waitKey(100) video_writer.write(img) video_writer.release() def video2picture(video_path, img_dir): vc = cv2.VideoCapture(video_path) c = 0 ret = vc.isOpened() while ret: c = c + 1 ret, frame = vc.read() if ret: img_path = f"{img_dir}/{c}.jpg" cv2.imwrite(img_path, frame) cv2.waitKey(1) else: break # 视频释放 vc.release() def check_or_create_dir(dir_path): if os.path.exists(dir_path): files = os.listdir(dir_path) if files: return False else: return True else: os.makedirs(dir_path, exist_ok=True) return True def video2char_video(video_path): video_dir = os.path.dirname(os.path.abspath(video_path)) img_dir = f"{video_dir}/img" char_img_dir = f"{video_dir}/char_img" output_dir = f'{video_dir}/output' img_dir_flag = check_or_create_dir(img_dir) char_img_dir_flag = check_or_create_dir(char_img_dir) output_dir_flag = check_or_create_dir(output_dir) if not img_dir_flag: messagebox.showerror('错误', f'{img_dir} 不是空的,请删除或者清空此目录') elif not char_img_dir_flag: messagebox.showerror('错误', f'{char_img_dir} 不是空的,请删除或者清空此目录') elif not output_dir_flag: messagebox.showerror('错误', f'{output_dir} 不是空的,请删除或者清空此目录') if img_dir_flag and char_img_dir_flag and output_dir_flag: video2picture(video_path, img_dir) origin_img_width, origin_img_height = picture2char_img(img_dir, char_img_dir) char_img2video(char_img_dir, output_dir, origin_img_width, origin_img_height) label["text"] = "转换完成" choose_file_button['state'] = NORMAL convert_button['state'] = NORMAL def choose_video_file_path(): global filepath filepath = filedialog.askopenfilename() def get_video_file_path(): if not filepath: messagebox.showerror('错误', '没有选择有效的路径,请重新选择') else: if os.path.isfile(filepath): choose_file_button['state'] = DISABLED convert_button['state'] = DISABLED label["text"] = "视频正在转换,请稍等!!!" t = threading.Thread(target=video2char_video, args=(filepath,)) t.setDaemon(True) t.start() else: messagebox.showerror('错误', '没有选择有效的路径,请重新选择') if __name__ == '__main__': root = tkinter.Tk() filepath = None label = tkinter.Label(root, text="欢迎使用海军专属的字符视频转换器") label.pack(side=TOP) choose_file_button = tkinter.Button(root, text="选择文件", command=choose_video_file_path) choose_file_button.pack(side=LEFT) convert_button = tkinter.Button(root, text="开始转换", command=get_video_file_path) convert_button.pack(side=RIGHT) screen_width, screen_height = root.maxsize() # 获取屏幕最大长宽 w = int((screen_width - 240) / 2) h = int((screen_height - 480) / 2) root.geometry(f'+{w}+{h}') root.resizable(width=False, height=False) root.title('字符视频转换器') root.mainloop()