视频转字符画
import cv2import tkinter as tk
import os.path as op
from tkinter import messagebox as mgb
from tkinter import ttk
from tkinter import filedialog as fdl
def video2strs(path_, root):
if not op.isfile(path_) or not path_.split(".") == "mp4":
mgb.showwarning(title="Warning", messge="目前仅支持MP4格式的转换!!")
return
dir_name, file_name = op.split(path_)
new_text_position = dir_name + r"/"+ file_name.split(".")
#os.chdir(dir_name)
_ = 0
suffix = ".v2s"
while op.isfile(new_text_position + suffix):
_ += 1
suffix = f"({_}).v2s"
new_text_position += suffix
print(new_text_position)
def get_resize_height():
_ = cv2.VideoCapture(path_)
ret, temp_frame = _.read()
temp_frame = cv2.resize(temp_frame, (0, 0), fx=0.09375,fy=0.046875)
__ = temp_frame.shape
_.release()
return __
def start_trans():
text_f = open(new_text_position, "w", encoding="utf-8")
text_f.write(str(get_resize_height()) + "\n")
#with open(new_text_position, "w", encoding="utf-8") as text_f:
def trans():
nonlocal count
if video.isOpened():
temp_text = ""
ret, current_frame = video.read()
if ret:
current_frame = cv2.resize(cv2.cvtColor(current_frame, cv2.COLOR_BGR2GRAY), (0, 0), fx=0.09375,fy=0.046875)
for i in range(current_frame.shape):
for j in range(current_frame.shape):
temp_text += ascii__ / 255 * 7)]
temp_text += '\n'
else:
text_f.write(temp_text)
if count > one_of_ten:
progress_bar["value"] += 10
progress_bar.pack(pady=(1, 2))
count = 0
show_progress.after(0, trans)
count += 1
#print("+1f")
else:
label1.config(text="OK!")
progress_bar["value"] = 100
progress_bar.pack(pady=(1, 2))
text_f.close()
tk.Button(show_progress, text="Quit", command=show_progress.destroy, width=5).pack(pady=15)
video.release()
video = cv2.VideoCapture(path_)
amount_of_frame = video.get(cv2.CAP_PROP_FRAME_COUNT)
one_of_ten = amount_of_frame // 10
temp_text = ""
ascii__ = ["@", "G·", "¥", "·*·", "·+", "·=", "-'-", "·_·"]
count = 0
show_progress.after(0, trans)
show_progress = tk.Tk()
show_progress.geometry("300x180")
label1 = tk.Label(show_progress, text="Patience!", justify="center")
progress_bar = ttk.Progressbar(show_progress, length=200, mode="determinate")
label1.pack(pady=(30, 1))
progress_bar.pack(pady=(1, 2))
show_progress.after(0, start_trans) # after方法 第二个参数之后是收集参数
class V2sTrans:
def __init__(self, root):
self.root = root
self.root.geometry(f"500x400+{self.root.winfo_screenwidth()//2-250}+{self.root.winfo_screenheight()//2-200}")
self.init_win()
def init_win(self):
def set_dir():
path_ = fdl.askopenfilename(title="选择视频文件", filetypes=[("MP4", '.mp4')])
dir_entry.delete(0, "end")
dir_entry.insert(0,path_)
def set_dir_play():
path_ = fdl.askopenfilename(title="选择v2s文件", filetypes=[("V2S", '.v2s')])
dir_entry_.delete(0, "end")
dir_entry_.insert(0, path_)
l_frame0 = tk.LabelFrame(master=self.root, text="V2S", height=30)
l_frame0.pack(fill='x', padx=20, pady=10, ipady=50)
l_frame1 = tk.LabelFrame(master=l_frame0, text="输入mp4文件路径")
l_frame1.pack(fill="x", padx=20, pady=(50, 0))
dir_entry = tk.Entry(master=l_frame1, width=40)
dir_choice_button = tk.Button(master=l_frame1, text="...", relief="raised", command=set_dir, width=4)
dir_entry.grid(row=0, column=0, padx=(45, 5), pady=(5,10))
dir_choice_button.grid(row=0, column=1, padx=(5, 10), pady=(5,10))
start_trans_button = tk.Button(master=l_frame0, text="转换", width=20,
command=lambda: video2strs(dir_entry.get(), self.root))
start_trans_button.pack(pady=10)
l_frame2 = tk.LabelFrame(master=l_frame0, text="输入v2s文件路径")
l_frame2.pack(fill="x", padx=20, pady=(20, 0))
dir_entry_ = tk.Entry(master=l_frame2, width=40)
dir_choice_button_ = tk.Button(master=l_frame2, text="...", relief="raised", command=set_dir_play, width=4)
dir_entry_.grid(row=0, column=0, padx=(45, 5), pady=(5,10))
dir_choice_button_.grid(row=0, column=1, padx=(5, 10), pady=(5,10))
start_trans_button = tk.Button(master=l_frame0, text="播放", width=20,
command=lambda: play_v2s(dir_entry_.get()))
start_trans_button.pack(pady=10)
def play_v2s(path_):
with open(path_, "r", encoding="utf-8") as f:
v2s = f.readlines()
height = int(v2s.split("\\"))
print(height)
v2s.pop(0)
frames = []
count = 0
temp_str = ""
for i in v2s:
if count == height:
count = 0
frames.append(temp_str)
temp_str = ""
temp_str += i
count += 1
frames_iter = iter(frames)
def update_frame():
label.config(text=next(frames_iter))
label.pack()
show_v2s.after(27, update_frame)
show_v2s = tk.Tk()
show_v2s.title("Playing")
#show_v2s.geometry(f"800x600+{show_v2s.winfo_screenwidth()//2-400}+{show_v2s.winfo_screenheight()//2-300}")
label = tk.Label(show_v2s, text="Ready", justify="center", width=1024, height=720, font=("微软雅黑", 6))
label.pack()
show_v2s.after(3000, update_frame)
show_v2s.mainloop()
if __name__ == '__main__':
root_ = tk.Tk()
root__ = V2sTrans(root_)
root__.root.mainloop() 首先,我来帮你修正这段视频转字符画的代码。主要问题包括语法错误、逻辑错误和未定义的变量。以下是修正后的代码,并附上关键说明。
关键修正点:
1. 文件扩展名检查:使用 path_.endswith('.mp4') 替代错误的 split 比较。
2. 文件路径拼接:正确获取文件名(不带扩展名)来生成输出文件路径。
3. 字符映射:定义了 ascii_chars 字符串,用于将灰度值映射到字符。
4. 帧处理:在转换每个帧时,正确遍历像素并映射到字符。
5. 进度条更新:使用 progress_bar['value'] 来更新进度。
6. v2s 文件解析:在播放部分,正确读取和分割帧数据。
修正后的代码:
import cv2
import tkinter as tk
import os.path as op
from tkinter import messagebox as mgb
from tkinter import ttk
from tkinter import filedialog as fdl
def video2strs(path_, root):
if not op.isfile(path_) or not path_.endswith('.mp4'):
mgb.showwarning(title="Warning", message="目前仅支持MP4格式的转换!!")
return
dir_name, file_name = op.split(path_)
file_base_name = op.splitext(file_name)# 获取不带扩展名的文件名
new_text_position = op.join(dir_name, file_base_name)# 使用 os.path.join 确保路径正确
_ = 0
suffix = ".v2s"
while op.isfile(new_text_position + suffix):
_ += 1
suffix = f"({_}).v2s"
new_text_position += suffix
print("输出文件路径:", new_text_position)
def get_resize_height():
cap = cv2.VideoCapture(path_)
ret, temp_frame = cap.read()
if ret:
# 调整帧大小,这里设置为宽度120,高度按比例计算
height, width = temp_frame.shape[:2]
new_width = 120
new_height = int(height * (new_width / width) * 0.5)# 高度减半以匹配字符画比例
temp_frame = cv2.resize(temp_frame, (new_width, new_height))
cap.release()
return (new_height, new_width)# 返回调整后的高度和宽度
cap.release()
return (0, 0)
def start_trans():
# 定义字符映射,用于将灰度值转换为字符
ascii_chars = "@%#*+=-:. "
text_f = open(new_text_position, "w", encoding="utf-8")
resize_dim = get_resize_height()
text_f.write(f"{resize_dim},{resize_dim}\n")# 写入高度和宽度,用逗号分隔
video = cv2.VideoCapture(path_)
amount_of_frame = int(video.get(cv2.CAP_PROP_FRAME_COUNT))
one_of_ten = max(amount_of_frame // 10, 1)# 避免除零
count = 0
def trans():
nonlocal count
if video.isOpened():
ret, current_frame = video.read()
if ret:
# 调整帧大小并转换为灰度图
current_frame = cv2.cvtColor(current_frame, cv2.COLOR_BGR2GRAY)
current_frame = cv2.resize(current_frame, (resize_dim, resize_dim))
temp_text = ""
for i in range(resize_dim):# 高度
for j in range(resize_dim):# 宽度
gray = current_frame
# 将灰度值映射到字符索引
index = int(gray / 255 * (len(ascii_chars) - 1))
temp_text += ascii_chars
temp_text += '\n'
text_f.write(temp_text)
count += 1
# 更新进度条
if count % one_of_ten == 0:
progress_bar['value'] += 10
progress_bar.update()
show_progress.after(1, trans)# 使用 after 继续处理下一帧
else:
# 处理完成
label1.config(text="OK!")
progress_bar['value'] = 100
progress_bar.update()
text_f.close()
video.release()
tk.Button(show_progress, text="Quit", command=show_progress.destroy, width=5).pack(pady=15)
else:
mgb.showerror("Error", "无法打开视频文件")
show_progress.after(0, trans)
# 创建进度窗口
show_progress = tk.Toplevel(root)# 使用 Toplevel 而非 Tk,避免多个主窗口
show_progress.geometry("300x180")
show_progress.title("转换进度")
label1 = tk.Label(show_progress, text="Patience!", justify="center")
progress_bar = ttk.Progressbar(show_progress, length=200, mode="determinate", maximum=100)
label1.pack(pady=(30, 1))
progress_bar.pack(pady=(1, 2))
show_progress.after(0, start_trans)
class V2sTrans:
def __init__(self, root):
self.root = root
self.root.geometry(f"500x400+{self.root.winfo_screenwidth()//2-250}+{self.root.winfo_screenheight()//2-200}")
self.root.title("视频转字符画")
self.init_win()
def init_win(self):
def set_dir():
path_ = fdl.askopenfilename(title="选择视频文件", filetypes=[("MP4 files", "*.mp4")])
if path_:
dir_entry.delete(0, "end")
dir_entry.insert(0, path_)
def set_dir_play():
path_ = fdl.askopenfilename(title="选择v2s文件", filetypes=[("V2S files", "*.v2s")])
if path_:
dir_entry_.delete(0, "end")
dir_entry_.insert(0, path_)
l_frame0 = tk.LabelFrame(master=self.root, text="V2S", height=30)
l_frame0.pack(fill='x', padx=20, pady=10, ipady=50)
l_frame1 = tk.LabelFrame(master=l_frame0, text="输入mp4文件路径")
l_frame1.pack(fill="x", padx=20, pady=(50, 0))
dir_entry = tk.Entry(master=l_frame1, width=40)
dir_choice_button = tk.Button(master=l_frame1, text="...", relief="raised", command=set_dir, width=4)
dir_entry.grid(row=0, column=0, padx=(45, 5), pady=(5,10))
dir_choice_button.grid(row=0, column=1, padx=(5, 10), pady=(5,10))
start_trans_button = tk.Button(master=l_frame0, text="转换", width=20,
command=lambda: video2strs(dir_entry.get(), self.root))
start_trans_button.pack(pady=10)
l_frame2 = tk.LabelFrame(master=l_frame0, text="输入v2s文件路径")
l_frame2.pack(fill="x", padx=20, pady=(20, 0))
dir_entry_ = tk.Entry(master=l_frame2, width=40)
dir_choice_button_ = tk.Button(master=l_frame2, text="...", relief="raised", command=set_dir_play, width=4)
dir_entry_.grid(row=0, column=0, padx=(45, 5), pady=(5,10))
dir_choice_button_.grid(row=0, column=1, padx=(5, 10), pady=(5,10))
start_play_button = tk.Button(master=l_frame0, text="播放", width=20,
command=lambda: play_v2s(dir_entry_.get()))
start_play_button.pack(pady=10)
def play_v2s(path_):
if not op.isfile(path_) or not path_.endswith('.v2s'):
mgb.showwarning(title="Warning", message="请选择有效的 .v2s 文件!")
return
with open(path_, "r", encoding="utf-8") as f:
lines = f.readlines()
if not lines:
mgb.showerror("Error", "文件为空或格式错误")
return
# 第一行是高度和宽度
first_line = lines.strip()
try:
height, width = map(int, first_line.split(','))
except:
mgb.showerror("Error", "文件格式错误:第一行应为高度,宽度")
return
# 移除第一行
char_lines = lines
# 将字符数据分割成帧
frames = []
temp_str = ""
count = 0
for line in char_lines:
if count < height:
temp_str += line
count += 1
if count == height:
frames.append(temp_str)
temp_str = ""
count = 0
# 检查是否有剩余数据
if temp_str:
frames.append(temp_str)
if not frames:
mgb.showerror("Error", "没有有效的帧数据")
return
frames_iter = iter(frames)
show_v2s = tk.Tk()
show_v2s.title("Playing")
# 设置窗口大小,根据字符画尺寸调整
label = tk.Label(show_v2s, text="Ready", justify="left", font=("Courier", 10))# 使用等宽字体
label.pack()
def update_frame():
try:
frame_text = next(frames_iter)
label.config(text=frame_text)
show_v2s.after(27, update_frame)# 约37帧每秒,但字符画可能较慢,可调整
except StopIteration:
label.config(text="播放完毕")
tk.Button(show_v2s, text="关闭", command=show_v2s.destroy).pack()
show_v2s.after(100, update_frame)# 延迟开始播放
show_v2s.mainloop()
if __name__ == '__main__':
root_ = tk.Tk()
app = V2sTrans(root_)
root_.mainloop()
注意事项:
1. 字符映射:使用了 ascii_chars = "@%#*+=-:. " 作为灰度到字符的映射,你可以根据需要调整字符顺序或数量。
2. 帧大小调整:在 get_resize_height 中,将帧宽度固定为120,高度按比例计算并减半,以适应字符画的比例(字符通常高度大于宽度)。
3. 进度条更新:使用 progress_bar['value'] += 10 来逐步更新进度。
4. 播放部分:使用 font=("Courier", 10) 等宽字体,以确保字符对齐。
5. 错误处理:添加了基本的错误检查,如文件是否存在、格式是否正确。
运行前请确保已安装 OpenCV:可以通过 pip install opencv-python 安装。如果遇到问题,请检查视频文件路径和输出目录的写入权限。
以上回复来自 FishC AI 小助理,如未能正确解答您的问题,请继续追问。 加一些效果演示的图片或动图会更好
页:
[1]