|
|
首先,我来帮你修正这段视频转字符画的代码。主要问题包括语法错误、逻辑错误和未定义的变量。以下是修正后的代码,并附上关键说明。
关键修正点:
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)[0] # 获取不带扩展名的文件名
- 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[0]},{resize_dim[1]}\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[1], resize_dim[0]))
- temp_text = ""
- for i in range(resize_dim[0]): # 高度
- for j in range(resize_dim[1]): # 宽度
- gray = current_frame[i, j]
- # 将灰度值映射到字符索引
- index = int(gray / 255 * (len(ascii_chars) - 1))
- temp_text += ascii_chars[index]
- 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[0].strip()
- try:
- height, width = map(int, first_line.split(','))
- except:
- mgb.showerror("Error", "文件格式错误:第一行应为高度,宽度")
- return
- # 移除第一行
- char_lines = lines[1:]
- # 将字符数据分割成帧
- 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 小助理,如未能正确解答您的问题,请继续追问。 |
|