| | |
| | | |
| | | 打包 exe |
| | | |
| | | 1、pyinstaller videosplitorV0.1.spec |
| | | 1、pyinstaller videosplitorV0.1.spec |
| | | |
| | | |
| | | |
| | | |
| | | PS. 添加本地文件夹到远程 git server: |
| | | |
| | | 1. 初始化本地 Git 仓库 |
| | | cd /path/to/your/folder |
| | | git init |
| | | |
| | | 2. 添加文件到 Git 仓库 |
| | | git add . |
| | | |
| | | 3. 提交文件到本地 Git 仓库 |
| | | git commit -m "Initial commit" |
| | | |
| | | 4. 连接到远程 Git 仓库 |
| | | git remote add origin ssh://wangzhibo@192.168.0.75:29418/Python_Video_Splitor.git |
| | | |
| | | 5. 推送本地代码到远程仓库 |
| | | git push -u origin master |
| | | |
| | | 6. 验证推送结果 |
| | | git remote -v |
| New file |
| | |
| | | import cv2 |
| | | import tkinter as tk |
| | | from tkinter import filedialog, ttk |
| | | from PIL import Image, ImageTk |
| | | |
| | | |
| | | class VideoPlayer: |
| | | def __init__(self, root): |
| | | self.root = root |
| | | self.root.title("Tkinter MP4 播放器") |
| | | |
| | | # 视频控件 |
| | | self.video_label = tk.Label(root) |
| | | self.video_label.pack() |
| | | |
| | | # 按钮和进度条区域 |
| | | controls_frame = tk.Frame(root) |
| | | controls_frame.pack() |
| | | |
| | | self.play_button = tk.Button(controls_frame, text="播放", command=self.toggle_play_pause) |
| | | self.play_button.grid(row=0, column=0, padx=5) |
| | | |
| | | self.replay_button = tk.Button(controls_frame, text="重新播放", command=self.replay_video) |
| | | self.replay_button.grid(row=0, column=1, padx=5) |
| | | |
| | | self.progress = ttk.Scale(controls_frame, from_=0, to=1, orient="horizontal", command=self.seek_video) |
| | | self.progress.grid(row=0, column=2, padx=5, sticky="ew") |
| | | |
| | | controls_frame.columnconfigure(2, weight=1) |
| | | |
| | | # 状态变量 |
| | | self.cap = None |
| | | self.is_playing = False |
| | | self.current_frame = 0 |
| | | self.total_frames = 0 |
| | | self.fps = 30 |
| | | self.video_path = None |
| | | |
| | | # 文件打开按钮 |
| | | self.open_button = tk.Button(root, text="打开视频", command=self.open_file) |
| | | self.open_button.pack() |
| | | |
| | | def open_file(self): |
| | | self.video_path = filedialog.askopenfilename(filetypes=[("MP4文件", "*.mp4"), ("所有文件", "*.*")]) |
| | | if not self.video_path: |
| | | return |
| | | self.load_video() |
| | | |
| | | def load_video(self): |
| | | if self.cap: |
| | | self.cap.release() |
| | | self.cap = cv2.VideoCapture(self.video_path) |
| | | if not self.cap.isOpened(): |
| | | tk.messagebox.showerror("错误", "无法打开视频") |
| | | return |
| | | self.fps = int(self.cap.get(cv2.CAP_PROP_FPS)) |
| | | self.total_frames = int(self.cap.get(cv2.CAP_PROP_FRAME_COUNT)) |
| | | self.current_frame = 0 |
| | | self.progress.config(to=self.total_frames) |
| | | self.is_playing = True |
| | | self.play_button.config(text="暂停") |
| | | self.play_video() |
| | | |
| | | def play_video(self): |
| | | if not self.is_playing or not self.cap: |
| | | return |
| | | |
| | | ret, frame = self.cap.read() |
| | | if ret: |
| | | self.current_frame += 1 |
| | | self.progress.set(self.current_frame) |
| | | frame = cv2.cvtColor(frame, cv2.COLOR_BGR2RGB) |
| | | img = ImageTk.PhotoImage(Image.fromarray(frame)) |
| | | self.video_label.config(image=img) |
| | | self.video_label.image = img |
| | | self.root.after(int(1000 / self.fps), self.play_video) |
| | | else: |
| | | self.is_playing = False |
| | | self.play_button.config(text="播放") |
| | | |
| | | def toggle_play_pause(self): |
| | | if not self.cap: |
| | | return |
| | | if self.is_playing: |
| | | self.is_playing = False |
| | | self.play_button.config(text="播放") |
| | | else: |
| | | self.is_playing = True |
| | | self.play_button.config(text="暂停") |
| | | self.play_video() |
| | | |
| | | def replay_video(self): |
| | | if not self.cap: |
| | | return |
| | | self.cap.set(cv2.CAP_PROP_POS_FRAMES, 0) |
| | | self.current_frame = 0 |
| | | self.progress.set(0) |
| | | self.is_playing = True |
| | | self.play_button.config(text="暂停") |
| | | self.play_video() |
| | | |
| | | def seek_video(self, value): |
| | | if not self.cap: |
| | | return |
| | | self.current_frame = int(float(value)) |
| | | self.cap.set(cv2.CAP_PROP_POS_FRAMES, self.current_frame) |
| | | if not self.is_playing: |
| | | self.play_video() |
| | | |
| | | |
| | | # 创建主窗口 |
| | | root = tk.Tk() |
| | | app = VideoPlayer(root) |
| | | root.mainloop() |
| New file |
| | |
| | | ffmpeg -i demo.mp4 -vf fps=30 -q:v 2 -f image2pipe - | ffmpeg -framerate 30 -f image2pipe -i - -c:v libx264 -preset slow -crf 18 -pix_fmt yuv420p output.mp4 |
| New file |
| | |
| | | import cv2 |
| | | import numpy as np |
| | | import subprocess |
| | | |
| | | def read_video_frames_to_memory(video_path, frame_rate=30): |
| | | """ |
| | | 使用 FFmpeg 将视频帧以 PNG 格式直接读取到内存中。 |
| | | """ |
| | | ffmpeg_command = [ |
| | | "ffmpeg", "-i", video_path, |
| | | "-vf", f"fps={frame_rate}", |
| | | "-f", "image2pipe", "-vcodec", "png", "-" |
| | | ] |
| | | |
| | | frame_size = 1080*1440*3 |
| | | |
| | | # 使用 subprocess 打开 FFmpeg 进程 |
| | | process = subprocess.Popen(ffmpeg_command, stdout=subprocess.PIPE, stderr=subprocess.PIPE, bufsize=frame_size**1) |
| | | |
| | | frames = [] |
| | | try: |
| | | while True: |
| | | # 读取单帧数据 |
| | | frame_bytes = process.stdout.read(frame_size) # 每次尝试读取 1MB |
| | | if not frame_bytes: |
| | | break |
| | | |
| | | # 解码 PNG 数据为 NumPy 数组(使用 OpenCV) |
| | | frame = cv2.imdecode(np.frombuffer(frame_bytes, np.uint8), cv2.IMREAD_UNCHANGED) |
| | | if frame is not None: |
| | | frames.append(frame) |
| | | finally: |
| | | process.stdout.close() |
| | | process.wait() |
| | | |
| | | return frames |
| | | |
| | | def write_frames_to_mp4(frames, output_path, frame_rate=30): |
| | | """ |
| | | 从帧数据生成 MP4 文件。 |
| | | """ |
| | | if not frames: |
| | | print("No frames to write.") |
| | | return |
| | | |
| | | # 获取帧尺寸 (高度, 宽度) |
| | | frame_height, frame_width = frames[0].shape[:2] |
| | | print(frame_width,frame_height) |
| | | |
| | | # 定义视频编码器和输出文件 |
| | | fourcc = cv2.VideoWriter_fourcc(*"mp4v") # MP4 格式编码器 |
| | | video_writer = cv2.VideoWriter(output_path, fourcc, frame_rate, (frame_width, frame_height)) |
| | | |
| | | for frame in frames: |
| | | # 检查帧是否是三通道 (RGB/BGR),如果需要转换格式 |
| | | if len(frame.shape) == 2: # 灰度图 |
| | | frame = cv2.cvtColor(frame, cv2.COLOR_GRAY2BGR) |
| | | elif frame.shape[2] == 4: # RGBA |
| | | frame = cv2.cvtColor(frame, cv2.COLOR_RGBA2BGR) |
| | | |
| | | video_writer.write(frame) |
| | | |
| | | video_writer.release() |
| | | print(f"Video saved to {output_path}") |
| | | |
| | | if __name__ == "__main__": |
| | | video_path = "E:/抖音-AI风景/Test/demo.mp4" |
| | | output_path = "output.mp4" |
| | | |
| | | # 读取视频帧到内存 |
| | | frames = read_video_frames_to_memory(video_path) |
| | | |
| | | # 将帧数据写入 MP4 文件 |
| | | write_frames_to_mp4(frames, output_path) |
| New file |
| | |
| | | import subprocess |
| | | import tkinter as tk |
| | | from tkinter import ttk |
| | | import threading |
| | | import time |
| | | |
| | | class VideoPlayer: |
| | | def __init__(self, root, video_path): |
| | | self.root = root |
| | | self.video_path = video_path |
| | | self.is_paused = False |
| | | self.is_running = True |
| | | |
| | | # 创建界面元素 |
| | | self.canvas = tk.Canvas(root, width=640, height=480) |
| | | self.canvas.pack() |
| | | |
| | | self.play_button = tk.Button(root, text="播放/暂停", command=self.toggle_pause) |
| | | self.play_button.pack(side=tk.LEFT) |
| | | |
| | | self.restart_button = tk.Button(root, text="重新播放", command=self.restart_video) |
| | | self.restart_button.pack(side=tk.LEFT) |
| | | |
| | | self.progress = ttk.Scale(root, from_=0, to=100, orient=tk.HORIZONTAL, command=self.update_position) |
| | | self.progress.pack(fill=tk.X, expand=True) |
| | | |
| | | # 启动播放视频线程 |
| | | self.video_thread = threading.Thread(target=self.play_video) |
| | | self.video_thread.start() |
| | | |
| | | def toggle_pause(self): |
| | | """切换播放/暂停状态""" |
| | | if self.is_paused: |
| | | self.is_paused = False |
| | | else: |
| | | self.is_paused = True |
| | | |
| | | def restart_video(self): |
| | | """重新开始播放""" |
| | | self.is_paused = False |
| | | self.progress.set(0) |
| | | self.play_video() |
| | | |
| | | def update_position(self, value): |
| | | """更新进度条位置""" |
| | | self.current_position = float(value) |
| | | |
| | | def play_video(self): |
| | | """播放视频并更新进度条""" |
| | | # subprocess.run 会阻塞,启动一个后台进程运行 ffplay |
| | | ffplay_cmd = ["ffplay", "-i", self.video_path, "-v", "quiet", "-x", "640", "-y", "480", "-autoexit"] |
| | | |
| | | # 创建 subprocess 来播放视频 |
| | | process = subprocess.Popen(ffplay_cmd, stdout=subprocess.PIPE, stderr=subprocess.PIPE) |
| | | |
| | | while self.is_running: |
| | | if self.is_paused: |
| | | continue |
| | | # 更新进度条 |
| | | self.update_progress(process) |
| | | time.sleep(0.1) # 控制更新频率 |
| | | |
| | | process.wait() # 等待视频播放结束 |
| | | self.restart_video() # 播放结束后自动重播 |
| | | |
| | | def update_progress(self, process): |
| | | """更新进度条""" |
| | | # 获取 ffplay 的输出并更新进度 |
| | | stdout, stderr = process.communicate() |
| | | lines = stderr.decode().splitlines() |
| | | for line in lines: |
| | | if "time=" in line: |
| | | # 提取时间信息 |
| | | current_time = line.split("time=")[-1].split(" ")[0] |
| | | hours, minutes, seconds = map(float, current_time.split(":")) |
| | | total_seconds = hours * 3600 + minutes * 60 + seconds |
| | | video_duration = 120 # 手动设置视频时长,或者通过其他方式获取时长 |
| | | progress_percentage = (total_seconds / video_duration) * 100 |
| | | self.progress.set(progress_percentage) |
| | | |
| | | def close(self): |
| | | """关闭视频播放器""" |
| | | self.is_running = False |
| | | self.video_thread.join() |
| | | |
| | | # 创建 Tkinter 窗口 |
| | | root = tk.Tk() |
| | | root.title("视频播放器") |
| | | |
| | | media_path = "E:/抖音-AI风景/data/关注/1515593476095344/视频/7375164244729302272.mp4" # 替换为实际视频文件路径 |
| | | player = VideoPlayer(root, media_path) |
| | | |
| | | root.protocol("WM_DELETE_WINDOW", player.close) |
| | | root.mainloop() |
| New file |
| | |
| | | import vlc |
| | | from pathlib import Path |
| | | import time |
| | | import os |
| | | |
| | | |
| | | class Loop_Vlc_Player: |
| | | |
| | | def __init__(self): |
| | | self.player = vlc.Instance() |
| | | |
| | | def addPlayList(self, localPath): |
| | | self.mediaList = self.player.media_list_new() |
| | | self.mediaList.add_media(localPath) |
| | | self.listPlayer = self.player.media_list_player_new() |
| | | self.listPlayer.set_media_list(self.mediaList) |
| | | self.listPlayer.set_playback_mode(vlc.PlaybackMode(1)) |
| | | |
| | | def play(self): |
| | | self.listPlayer.play() |
| | | |
| | | def stop(self): |
| | | self.listPlayer.stop() |
| | | |
| | | |
| | | play = Loop_Vlc_Player() |
| | | video_path = "E:/抖音-AI风景/data/关注/1515593476095344/视频/7375164244729302272.mp4" # 替换为实际视频文件路径 |
| | | |
| | | play.addPlayList(video_path) |
| | | play.play() |
| | | while True: |
| | | pass |
| | | |
| New file |
| | |
| | | import cv2 |
| | | import os |
| | | |
| | | def slice_video_by_frames(video_path, frame_segments, output_dir, codec="mp4v", fps=None): |
| | | """ |
| | | 使用 OpenCV 实现视频按帧切分。 |
| | | |
| | | 参数: |
| | | - video_path: 输入视频路径。 |
| | | - frame_segments: 帧区间的列表,例如:[(start_frame1, end_frame1), (start_frame2, end_frame2), ...]。 |
| | | - output_dir: 输出文件夹路径。 |
| | | - codec: 视频编码器,默认是 "mp4v"(适合生成 MP4)。 |
| | | - fps: 帧率,默认为输入视频的帧率。 |
| | | """ |
| | | # 创建输出目录 |
| | | os.makedirs(output_dir, exist_ok=True) |
| | | |
| | | # 打开视频 |
| | | video_capture = cv2.VideoCapture(video_path) |
| | | if not video_capture.isOpened(): |
| | | raise ValueError(f"无法打开视频文件: {video_path}") |
| | | |
| | | # 获取视频属性 |
| | | original_fps = video_capture.get(cv2.CAP_PROP_FPS) |
| | | frame_width = int(video_capture.get(cv2.CAP_PROP_FRAME_WIDTH)) |
| | | frame_height = int(video_capture.get(cv2.CAP_PROP_FRAME_HEIGHT)) |
| | | total_frames = int(video_capture.get(cv2.CAP_PROP_FRAME_COUNT)) |
| | | |
| | | if fps is None: |
| | | fps = original_fps |
| | | |
| | | print(f"视频属性:总帧数={total_frames}, 帧率={original_fps}, 分辨率={frame_width}x{frame_height}") |
| | | |
| | | for idx, (start_frame, end_frame) in enumerate(frame_segments): |
| | | # 校验帧范围 |
| | | if start_frame < 0 or end_frame > total_frames or start_frame >= end_frame: |
| | | print(f"无效的帧区间:({start_frame}, {end_frame})") |
| | | continue |
| | | |
| | | # 输出文件路径 |
| | | output_file = os.path.join(output_dir, f"slice_{idx+1}.mp4") |
| | | |
| | | # 初始化视频写入器 |
| | | fourcc = cv2.VideoWriter_fourcc(*codec) |
| | | video_writer = cv2.VideoWriter(output_file, fourcc, fps, (frame_width, frame_height)) |
| | | |
| | | # 跳转到起始帧 |
| | | video_capture.set(cv2.CAP_PROP_POS_FRAMES, start_frame) |
| | | |
| | | # 逐帧读取并写入 |
| | | for frame_idx in range(start_frame, end_frame): |
| | | ret, frame = video_capture.read() |
| | | if not ret: |
| | | print(f"无法读取帧:{frame_idx}") |
| | | break |
| | | video_writer.write(frame) |
| | | |
| | | video_writer.release() |
| | | print(f"生成切片: {output_file} ({start_frame} ~ {end_frame})") |
| | | |
| | | video_capture.release() |
| | | print("视频切分完成。") |
| | | |
| | | # 示例用法 |
| | | if __name__ == "__main__": |
| | | video_path = "E:/抖音-AI风景/Test/demo.mp4" # 替换为你的输入视频路径 |
| | | output_dir = "output" # 替换为你的输出文件夹路径 |
| | | frame_segments = [(0, 60),(60, 79), (79,98),(98,116), (116,135),(135, 153), (153,172), (172,190), (190,209), (209,228), (228,247), (247,266), (266,285),(285,305)] # 定义帧区间 |
| | | |
| | | slice_video_by_frames(video_path, frame_segments, output_dir) |
| New file |
| | |
| | | import subprocess |
| | | import os |
| | | |
| | | def split_video_by_pipe(video_path, frame_segments, width, height, output_dir): |
| | | """ |
| | | 使用 FFmpeg 的管道方式,将视频拆分成指定的帧切片,输出为多个视频文件。 |
| | | |
| | | 参数: |
| | | - video_path: 输入视频路径 |
| | | - frame_segments: 切片帧段,例如:[0, M1, M2, ..., Mlast] |
| | | - width, height: 视频分辨率,用于计算帧大小 |
| | | - output_dir: 输出文件目录 |
| | | """ |
| | | # 视频帧格式假设 RGB(每像素 3 字节) |
| | | bytes_per_pixel = 3 |
| | | frame_size = width * height * bytes_per_pixel # 单帧的字节大小 |
| | | |
| | | # 创建输出子目录 |
| | | os.makedirs(output_dir, exist_ok=True) |
| | | |
| | | # 打开视频解码管道,用于帧提取 |
| | | ffmpeg_extract = [ |
| | | "ffmpeg", "-i", video_path, "-vf", "fps=30", "-f", "image2pipe", "-vcodec", "rawvideo", "-" |
| | | ] |
| | | process_extract = subprocess.Popen(ffmpeg_extract, stdout=subprocess.PIPE, bufsize=frame_size * 1) |
| | | |
| | | # 创建多个帧分段管道 |
| | | slice_pipes = [] |
| | | for i, _ in enumerate(frame_segments[:-1]): |
| | | output_file = os.path.join(output_dir, f"slice_{i+1}.mp4") |
| | | ffmpeg_encode = [ |
| | | "ffmpeg", "-framerate", "30", "-f", "rawvideo", "-vcodec", "rawvideo", "-s", f"{width}x{height}", "-pix_fmt", "rgb24", "-i", "-", |
| | | "-c:v", "libx264", "-preset", "slow", "-crf", "18", "-pix_fmt", "yuv420p", output_file |
| | | ] |
| | | slice_pipes.append(subprocess.Popen(ffmpeg_encode, stdin=subprocess.PIPE, bufsize=frame_size * 1)) |
| | | |
| | | # 读取帧并分配到对应的切片管道 |
| | | current_frame = 0 |
| | | while True: |
| | | frame_data = process_extract.stdout.read(frame_size) |
| | | if len(frame_data) < frame_size: |
| | | break # 读取结束 |
| | | |
| | | # 找到当前帧所属的切片段 |
| | | for i, (start_frame, end_frame) in enumerate(zip(frame_segments[:-1], frame_segments[1:])): |
| | | if start_frame <= current_frame < end_frame: |
| | | print(i) |
| | | slice_pipes[i].stdin.write(frame_data) |
| | | break |
| | | |
| | | current_frame += 1 |
| | | |
| | | # 关闭所有管道 |
| | | process_extract.stdout.close() |
| | | process_extract.wait() |
| | | for pipe in slice_pipes: |
| | | pipe.stdin.close() |
| | | pipe.wait() |
| | | |
| | | print(f"视频分段完成,切片存储在:{output_dir}") |
| | | |
| | | |
| | | |
| | | # 示例使用 |
| | | video_path = "E:/抖音-AI风景/Test/demo.mp4" |
| | | frame_segments = [0, 60, 69, 305] |
| | | fps = 30 |
| | | width, height = 1080, 1440 |
| | | output_dir = "output" |
| | | |
| | | split_video_by_pipe(video_path, frame_segments, width,height, output_dir) |
| New file |
| | |
| | | import subprocess |
| | | import os |
| | | |
| | | def test_pipeline_to_multiple_slices(video_path): |
| | | frame_segments = [(0, 60), (60, 79), (79, 98), (98, 116), (116, 135), |
| | | (135, 153), (153, 172), (172, 190), (190, 209), |
| | | (209, 228), (228, 247), (247, 266), (266, 285), (285, 305)] # 帧区间 |
| | | |
| | | # 启动 FFmpeg 以读取视频帧数据并通过管道输出 |
| | | ffmpeg_extract = [ |
| | | "ffmpeg", "-i", video_path, "-vf", "fps=30", "-f", "image2pipe", "-vcodec", "png", "-" |
| | | ] |
| | | process_extract = subprocess.Popen(ffmpeg_extract, stdout=subprocess.PIPE, bufsize=10**8) # 使用较大缓冲区 |
| | | |
| | | # 创建多个子进程,处理视频切片 |
| | | slice_processes = [] |
| | | output_pipes = [] |
| | | |
| | | for i, (start_frame, end_frame) in enumerate(frame_segments): |
| | | output_file = f"slice_{i+1}.mp4" |
| | | ffmpeg_encode = [ |
| | | "ffmpeg", "-framerate", "30", "-f", "image2pipe", "-vcodec", "png", "-i", "-", |
| | | "-c:v", "libx264", "-preset", "slow", "-crf", "0", "-pix_fmt", "yuv420p", output_file |
| | | ] |
| | | process = subprocess.Popen(ffmpeg_encode, stdin=subprocess.PIPE) |
| | | slice_processes.append(process) |
| | | output_pipes.append((process.stdin, start_frame, end_frame)) |
| | | |
| | | current_frame = 0 |
| | | |
| | | try: |
| | | while True: |
| | | frame_data = process_extract.stdout.read(1024*1024) # 每次读取 1MB,直到无数据 |
| | | if not frame_data: |
| | | break # 当读取到视频结束时跳出循环 |
| | | |
| | | # 分发帧数据到合适的子进程 |
| | | for pipe, start_frame, end_frame in output_pipes: |
| | | if start_frame <= current_frame < end_frame: |
| | | pipe.write(frame_data) # 写入帧数据 |
| | | break |
| | | |
| | | current_frame += 1 |
| | | |
| | | finally: |
| | | # 关闭所有子进程的输入流 |
| | | for pipe, _, _ in output_pipes: |
| | | pipe.close() |
| | | |
| | | # 等待所有子进程完成 |
| | | for process in slice_processes: |
| | | process.wait() |
| | | |
| | | # 关闭提取进程的输出流 |
| | | process_extract.stdout.close() |
| | | process_extract.wait() |
| | | |
| | | if __name__ == "__main__": |
| | | video_path = "E:/抖音-AI风景/Test/demo.mp4" |
| | | test_pipeline_to_multiple_slices(video_path) |
| New file |
| | |
| | | import pygame |
| | | import subprocess |
| | | import threading |
| | | import time |
| | | import tkinter as tk |
| | | from tkinter import ttk |
| | | |
| | | class VideoPlayer: |
| | | def __init__(self, root, video_path): |
| | | self.root = root |
| | | self.video_path = video_path |
| | | self.is_paused = False |
| | | self.is_running = True |
| | | self.current_position = 0 |
| | | |
| | | # 初始化 pygame |
| | | pygame.init() |
| | | self.screen = pygame.display.set_mode((640, 480)) |
| | | pygame.display.set_caption("视频播放器") |
| | | |
| | | # 创建 Tkinter 控件 |
| | | self.canvas = tk.Canvas(root, width=640, height=480) |
| | | self.canvas.pack() |
| | | |
| | | self.play_button = tk.Button(root, text="播放/暂停", command=self.toggle_pause) |
| | | self.play_button.pack(side=tk.LEFT) |
| | | |
| | | self.restart_button = tk.Button(root, text="重新播放", command=self.restart_video) |
| | | self.restart_button.pack(side=tk.LEFT) |
| | | |
| | | self.progress = ttk.Scale(root, from_=0, to=100, orient=tk.HORIZONTAL, command=self.update_position) |
| | | self.progress.pack(fill=tk.X, expand=True) |
| | | |
| | | # 启动播放视频线程 |
| | | self.video_thread = threading.Thread(target=self.play_video) |
| | | self.video_thread.start() |
| | | |
| | | def toggle_pause(self): |
| | | """切换播放/暂停状态""" |
| | | if self.is_paused: |
| | | self.is_paused = False |
| | | else: |
| | | self.is_paused = True |
| | | |
| | | def restart_video(self): |
| | | """重新开始播放""" |
| | | self.is_paused = False |
| | | self.progress.set(0) |
| | | self.play_video() |
| | | |
| | | def update_position(self, value): |
| | | """更新进度条位置""" |
| | | self.current_position = float(value) |
| | | |
| | | def play_video(self): |
| | | """播放视频并更新进度条""" |
| | | # 使用 subprocess 播放视频并提取帧 |
| | | ffmpeg_cmd = [ |
| | | "ffmpeg", "-i", self.video_path, "-f", "image2pipe", "-pix_fmt", "rgb24", |
| | | "-vcodec", "rawvideo", "-"] |
| | | |
| | | # 创建 subprocess 来执行 ffmpeg 命令 |
| | | process = subprocess.Popen(ffmpeg_cmd, stdout=subprocess.PIPE, stderr=subprocess.PIPE) |
| | | |
| | | frame_count = 0 |
| | | while self.is_running: |
| | | if self.is_paused: |
| | | time.sleep(0.1) |
| | | continue |
| | | |
| | | # 从 ffmpeg 提取一帧 |
| | | raw_frame = process.stdout.read(640 * 480 * 3) # 读取一帧(RGB24) |
| | | if len(raw_frame) < 640 * 480 * 3: |
| | | break # 如果读取的帧不完整,则停止 |
| | | |
| | | frame = pygame.image.fromstring(raw_frame, (640, 480), 'RGB') |
| | | self.screen.blit(frame, (0, 0)) |
| | | pygame.display.flip() # 更新屏幕 |
| | | |
| | | frame_count += 1 |
| | | self.update_progress(frame_count) |
| | | |
| | | time.sleep(0.04) # 控制帧率 (假设视频的帧率是 25 FPS) |
| | | |
| | | process.wait() # 等待视频播放结束 |
| | | self.restart_video() # 播放结束后自动重播 |
| | | |
| | | def update_progress(self, frame_count): |
| | | """更新进度条""" |
| | | video_duration = 120 # 手动设置视频时长,或者通过其他方式获取时长 |
| | | progress_percentage = (frame_count / (25 * video_duration)) * 100 |
| | | self.progress.set(progress_percentage) |
| | | |
| | | def close(self): |
| | | """关闭视频播放器""" |
| | | self.is_running = False |
| | | self.video_thread.join() |
| | | |
| | | # 创建 Tkinter 窗口 |
| | | root = tk.Tk() |
| | | root.title("视频播放器") |
| | | |
| | | media_path = "E:/抖音-AI风景/data/关注/1515593476095344/视频/7375164244729302272.mp4" # 替换为实际视频文件路径 |
| | | player = VideoPlayer(root, media_path) |
| | | |
| | | root.protocol("WM_DELETE_WINDOW", player.close) |
| | | root.mainloop() |
| New file |
| | |
| | | import tkinter as tk |
| | | from tkinter import filedialog |
| | | |
| | | def open_folder(): |
| | | folder_path = filedialog.askdirectory() |
| | | if folder_path: |
| | | # 假设计算文件数量 |
| | | file_count = 5 # 示例值 |
| | | print("正在更新文件数量...") |
| | | file_count_label.config(text="文件数量:" + str(file_count)) |
| | | root.update() # 刷新界面 |
| | | |
| | | root = tk.Tk() |
| | | root.geometry("400x200") |
| | | |
| | | # 添加一个 Label |
| | | file_count_label = tk.Label(root, text="文件数量:0") |
| | | file_count_label.pack(pady=10) |
| | | |
| | | # 添加按钮 |
| | | open_button = tk.Button(root, text="打开文件夹", command=open_folder) |
| | | open_button.pack(pady=20) |
| | | |
| | | root.mainloop() |
| New file |
| | |
| | | import cv2 |
| | | from scenedetect import detect, ContentDetector |
| | | from moviepy.editor import VideoFileClip |
| | | |
| | | import sys |
| | | # 检测视频中的场景 |
| | | scene_list = detect(sys.argv[1], ContentDetector()) |
| | | |
| | | frame_list = [] |
| | | |
| | | for i, scene in enumerate(scene_list): |
| | | if i==0: |
| | | print(scene[0].get_frames(),scene[1].get_frames()+2) |
| | | frame_list.append([scene[0].get_frames(),scene[1].get_frames()+2]) |
| | | elif i==len(scene_list)-1: |
| | | print(scene[0].get_frames()+2,scene[1].get_frames()) |
| | | frame_list.append([scene[0].get_frames()+2,scene[1].get_frames()]) |
| | | else: |
| | | print(scene[0].get_frames()+2,scene[1].get_frames()+2) |
| | | frame_list.append([scene[0].get_frames()+2,scene[1].get_frames()+2]) |
| | | |
| | | |
| | | # 打开原始视频 |
| | | video_clip = VideoFileClip(sys.argv[1]) |
| | | |
| | | # 遍历场景并保存为新的 MP4 文件 |
| | | for i, (start_frame, end_frame) in enumerate(scene_list): |
| | | # 将帧数转换为秒 |
| | | start_time = start_frame.get_seconds() # 使用 get_seconds() 转换为秒 |
| | | end_time = end_frame.get_seconds() # 使用 get_seconds() 转换为秒 |
| | | |
| | | # 切割视频 |
| | | scene_clip = video_clip.subclip(start_time, end_time) |
| | | |
| | | # 设置输出文件名 |
| | | output_filename = f"oringal_{i+1}.mp4" |
| | | |
| | | # 保存场景为 MP4 文件 |
| | | scene_clip.write_videofile(output_filename, codec="libx264", fps=video_clip.fps) |
| | | |
| | | print(f"Scene {i+1} has been saved as {output_filename}") |
| | | |
| | | # 关闭视频文件 |
| | | video_clip.close() |
| | | |
| | | # 打开原始视频 |
| | | video_clip = VideoFileClip(sys.argv[1]) |
| | | fps =video_clip.fps |
| | | # 遍历场景并保存为新的 MP4 文件 |
| | | for index,item in enumerate(frame_list): |
| | | # 将帧数转换为秒 |
| | | start_time = item[0]/fps |
| | | end_time = item[1]/fps |
| | | |
| | | # 切割视频 |
| | | scene_clip = video_clip.subclip(start_time, end_time) |
| | | |
| | | # 设置输出文件名 |
| | | output_filename = f"modified_{index+1}.mp4" |
| | | |
| | | # 保存场景为 MP4 文件 |
| | | scene_clip.write_videofile(output_filename, codec="libx264", fps=video_clip.fps) |
| | | |
| | | print(f"Scene {i+1} has been saved as {output_filename}") |
| | | |
| | | # 关闭视频文件 |
| | | video_clip.close() |
| | | |
| | | |
| New file |
| | |
| | | import cv2 |
| | | from scenedetect import detect, ContentDetector |
| | | |
| | | # 检测视频中的场景 |
| | | scene_list = detect('demo3.mp4', ContentDetector()) |
| | | |
| | | # 存储修改后的场景信息 |
| | | modified_scene_list = [] |
| | | |
| | | # 遍历场景 |
| | | for i, scene in enumerate(scene_list): |
| | | if i == 0: |
| | | # 第一个场景直接添加 |
| | | modified_scene_list.append(scene) |
| | | elif i == len(scene_list) - 1: |
| | | # 最后一个场景不处理 |
| | | modified_scene_list.append(scene) |
| | | else: |
| | | # 从第二个场景开始,处理前两帧 |
| | | current_scene_start_frame = scene[0].get_frames() |
| | | current_scene_end_frame = scene[1].get_frames() |
| | | |
| | | # 获取前一个场景 |
| | | previous_scene = modified_scene_list[-1] |
| | | |
| | | # 获取前一个场景的结束帧 |
| | | previous_scene_end_frame = previous_scene[1].get_frames() |
| | | |
| | | # 更新前一个场景的结束帧,将当前场景的前两帧拼接到前一个场景后面 |
| | | modified_previous_scene_end_frame = previous_scene_end_frame + 2 |
| | | |
| | | # 更新前一个场景的结束时间 |
| | | modified_scene_list[-1] = (previous_scene[0], scene[0]) |
| | | |
| | | # 添加当前场景(去掉前两帧) |
| | | modified_scene_list.append((scene[0], scene[1])) |
| | | |
| | | # 打开原始视频 |
| | | cap = cv2.VideoCapture('demo3.mp4') |
| | | fps = cap.get(cv2.CAP_PROP_FPS) # 获取帧率 |
| | | frame_width = int(cap.get(cv2.CAP_PROP_FRAME_WIDTH)) # 获取帧宽度 |
| | | frame_height = int(cap.get(cv2.CAP_PROP_FRAME_HEIGHT)) # 获取帧高度 |
| | | |
| | | # 对每个场景进行切割并保存为 MP4 |
| | | for i, scene in enumerate(modified_scene_list): |
| | | start_frame = scene[0].get_frames() |
| | | end_frame = scene[1].get_frames() |
| | | |
| | | # 设置视频读取的起始位置 |
| | | cap.set(cv2.CAP_PROP_POS_FRAMES, start_frame) |
| | | |
| | | # 创建一个 VideoWriter 对象,用于保存当前场景为新的 MP4 文件 |
| | | output_filename = f'scene_{i+1}.mp4' |
| | | fourcc = cv2.VideoWriter_fourcc(*'H264') # 使用 H.264 编码器 |
| | | out = cv2.VideoWriter(output_filename, fourcc, fps, (frame_width, frame_height)) # 视频分辨率 |
| | | |
| | | # 读取每一帧并写入到输出文件中 |
| | | for frame_num in range(start_frame, end_frame): |
| | | ret, frame = cap.read() |
| | | if not ret: |
| | | break |
| | | out.write(frame) |
| | | |
| | | # 释放 VideoWriter 和 VideoCapture 对象 |
| | | out.release() |
| | | |
| | | print(f'Scene {i+1} has been saved as {output_filename}') |
| | | |
| | | # 释放 VideoCapture 对象 |
| | | cap.release() |
| New file |
| | |
| | | from scenedetect import detect, ContentDetector, split_video_ffmpeg |
| | | scene_list = detect('demo.mp4', ContentDetector()) |
| | | split_video_ffmpeg('demo.mp4', scene_list) |
| New file |
| | |
| | | import cv2 |
| | | from skimage.metrics import structural_similarity as ssim |
| | | import numpy as np |
| | | |
| | | class VideoSSIM: |
| | | def __init__(self, video_path): |
| | | # 初始化视频路径和打开视频 |
| | | self.video_path = video_path |
| | | self.cap = cv2.VideoCapture(video_path) |
| | | if not self.cap.isOpened(): |
| | | raise ValueError(f"Error opening video file: {video_path}") |
| | | |
| | | # 获取视频帧率(fps) |
| | | self.fps = self.cap.get(cv2.CAP_PROP_FPS) |
| | | |
| | | # 获取视频的尺寸 |
| | | self.frame_width = int(self.cap.get(cv2.CAP_PROP_FRAME_WIDTH)) |
| | | self.frame_height = int(self.cap.get(cv2.CAP_PROP_FRAME_HEIGHT)) |
| | | |
| | | def calculate_ssim(self): |
| | | # 读取视频的第一帧 |
| | | ret, prev_frame = self.cap.read() |
| | | if not ret: |
| | | raise ValueError("Error reading the first frame") |
| | | |
| | | prev_frame_gray = cv2.cvtColor(prev_frame, cv2.COLOR_BGR2GRAY) # 转换为灰度图 |
| | | |
| | | # 存储相似性数值 |
| | | ssim_values = [] |
| | | |
| | | # 从第二帧开始逐帧计算与前一帧的结构相似性 |
| | | frame_index = 2 # 从第二帧开始 |
| | | while True: |
| | | ret, curr_frame = self.cap.read() |
| | | if not ret: |
| | | break |
| | | |
| | | # 转换当前帧为灰度图 |
| | | curr_frame_gray = cv2.cvtColor(curr_frame, cv2.COLOR_BGR2GRAY) |
| | | |
| | | # 计算SSIM |
| | | ssim_value, _ = ssim(prev_frame_gray, curr_frame_gray, full=True) |
| | | ssim_values.append((frame_index, ssim_value)) # 记录帧索引和SSIM值 |
| | | |
| | | # 更新前一帧 |
| | | prev_frame_gray = curr_frame_gray |
| | | frame_index += 1 |
| | | |
| | | # 释放视频捕捉对象 |
| | | self.cap.release() |
| | | |
| | | return ssim_values |
| | | |
| | | def save_ssim_to_file(self, output_file="ssim_values.txt"): |
| | | ssim_values = self.calculate_ssim() |
| | | |
| | | # 保存SSIM值到文件 |
| | | with open(output_file, "w") as f: |
| | | for frame_idx, ssim_value in ssim_values: |
| | | f.write(f"Frame {frame_idx}: SSIM = {ssim_value}\n") |
| | | |
| | | print(f"SSIM values saved to {output_file}") |
| | | |
| | | # 使用示例 |
| | | video_ssim = VideoSSIM("demo.mp4") # 替换成你的视频文件路径 |
| | | video_ssim.save_ssim_to_file("demo_ssim_output.txt") |
| New file |
| | |
| | | import cv2 |
| | | import numpy as np |
| | | import os |
| | | import time |
| | | |
| | | def read_video_to_memory(video_path): |
| | | """ |
| | | 读取 MP4 视频并将每一帧存储到内存中 |
| | | """ |
| | | start_time = time.time() |
| | | cap = cv2.VideoCapture(video_path) |
| | | frames = [] |
| | | |
| | | # 获取视频的总帧数和帧率 |
| | | total_frames = int(cap.get(cv2.CAP_PROP_FRAME_COUNT)) |
| | | fps = cap.get(cv2.CAP_PROP_FPS) |
| | | |
| | | print(total_frames) |
| | | |
| | | print(fps) |
| | | |
| | | while True: |
| | | ret, frame = cap.read() |
| | | if not ret: |
| | | break |
| | | frames.append(frame) |
| | | |
| | | cap.release() |
| | | print(time.time() -start_time ) |
| | | return frames, fps, total_frames |
| | | |
| | | def generate_thumbnail(frame, max_size=144): |
| | | """ |
| | | 生成不超过指定大小的缩略图 |
| | | """ |
| | | height, width = frame.shape[:2] |
| | | |
| | | # 确定缩放比例 |
| | | scale = min(max_size / height, max_size / width) |
| | | new_width = int(width * scale) |
| | | new_height = int(height * scale) |
| | | |
| | | # 缩放并返回缩略图 |
| | | thumbnail = cv2.resize(frame, (new_width, new_height)) |
| | | return thumbnail |
| | | |
| | | def save_video_slice(frames, slice_range, output_file, fps): |
| | | """ |
| | | 根据指定的帧范围将视频保存为新的片段 |
| | | """ |
| | | fourcc = cv2.VideoWriter_fourcc(*'mp4v') # 用于生成 MP4 格式 |
| | | height, width = frames[0].shape[:2] |
| | | out = cv2.VideoWriter(output_file, fourcc, fps, (width, height)) |
| | | |
| | | start_frame, end_frame = slice_range |
| | | |
| | | # 遍历指定范围的帧并写入到新视频中 |
| | | for i in range(start_frame, end_frame): |
| | | out.write(frames[i]) |
| | | |
| | | out.release() |
| | | |
| | | def create_video_slice_from_memory(video_path, slice_segments, output_dir): |
| | | """ |
| | | 从内存中的视频帧创建多个视频片段 |
| | | """ |
| | | frames, fps, total_frames = read_video_to_memory(video_path) |
| | | |
| | | start_time = time.time() |
| | | # 生成缩略图并显示 |
| | | thumbnails = [] |
| | | for frame in frames: |
| | | thumbnails.append(generate_thumbnail(frame)) |
| | | |
| | | print(time.time()-start_time) |
| | | |
| | | |
| | | # 根据切片分割视频并保存 |
| | | for i, (start_frame, end_frame) in enumerate(slice_segments): |
| | | output_file = os.path.join(output_dir, f"slice_{i+1}.mp4") |
| | | start_time = time.time()-start_time |
| | | save_video_slice(frames, (start_frame, end_frame), output_file, fps) |
| | | print(time.time()-start_time) |
| | | |
| | | |
| | | # 示例:读取视频并创建多个切片 |
| | | video_path = "E:/抖音-AI风景/Test/demo.mp4" |
| | | slice_segments = [(0, 60),(60, 79), (79,98),(98,116), (116,135),(135, 153), (153,172), (172,190), (190,209), (209,228), (228,247), (247,266), (266,285),(285,303)] # 定义帧区间 |
| | | output_dir = "output_slices" |
| | | |
| | | if not os.path.exists(output_dir): |
| | | os.makedirs(output_dir) |
| | | |
| | | create_video_slice_from_memory(video_path, slice_segments, output_dir) |
| New file |
| | |
| | | import subprocess |
| | | import os |
| | | |
| | | import re |
| | | |
| | | |
| | | def cut_video_by_frames(video_path, frame_segments, total_frames, output_dir): |
| | | """ |
| | | 将视频按照指定的帧切片进行切割。 |
| | | |
| | | 参数: |
| | | - video_path: 输入视频的路径 |
| | | - frame_segments: 切片的帧序号数组,例如:[0, M1, M2, ..., Mlast] |
| | | - output_dir: 输出视频文件的目录 |
| | | """ |
| | | fps = 30 # default fps |
| | | |
| | | # 获取视频的帧率 (fps) |
| | | command = [ |
| | | "ffmpeg", "-i", video_path, "-vf", "fps=1", "-f", "null", "-" |
| | | ] |
| | | result = subprocess.run(command, stdout=subprocess.PIPE, |
| | | stderr=subprocess.PIPE, text=True) |
| | | output = result.stderr |
| | | # 解析 fps,查找信息 |
| | | fps_line = next(line for line in output.splitlines() if 'fps' in line) |
| | | # 使用正则表达式提取帧率值(假设fps值总是出现在"fps"前面) |
| | | match = re.search(r'(\d+(\.\d+)?)\s*fps', fps_line) |
| | | if match: |
| | | fps = float(match.group(1)) # 提取并转换为 float |
| | | print(f"FPS: {fps}") |
| | | |
| | | print(f"视频的总帧数: {total_frames}, 帧率: {fps}") |
| | | |
| | | # 遍历切片区间 |
| | | for i in range(len(frame_segments) - 1): |
| | | start_frame = frame_segments[i] |
| | | end_frame = frame_segments[i + 1] |
| | | |
| | | |
| | | start_time = float(start_frame) / fps |
| | | end_time = float(end_frame) / fps |
| | | |
| | | output_file = os.path.join(output_dir, f"segment_{i + 1}.mp4") |
| | | |
| | | |
| | | # 使用 FFmpeg 命令来切割视频 |
| | | command = [ |
| | | "ffmpeg", "-i", video_path, "-ss", f"{start_time:.10f}", "-to", f"{end_time:.10f}", |
| | | "-c:v", "libx264", "-c:a", "copy","-crf", "18","-preset", "veryslow", output_file |
| | | ] |
| | | print(" ".join(command)) |
| | | |
| | | subprocess.run(command) |
| | | |
| | | # 最后一段处理(从最后一个切片到视频结束) |
| | | last_start_frame = frame_segments[-1] |
| | | last_start_time = last_start_frame / fps |
| | | output_file = os.path.join( |
| | | output_dir, f"segment_{len(frame_segments)}.mp4") |
| | | |
| | | #print(f"正在切割最后一段:从 {last_start_time:.10f}s 到视频结束,保存为 {output_file}") |
| | | command = [ |
| | | "ffmpeg", "-i", video_path, "-ss", f"{last_start_time:.10f}", |
| | | "-c:v", "libx264", "-c:a", "copy","-crf", "18","-preset", "veryslow", output_file |
| | | ] |
| | | print(" ".join(command)) |
| | | subprocess.run(command) |
| | | |
| | | |
| | | target_file = "E:/抖音-AI风景/新建文件夹\_demoold.mp4" |
| | | selected_labels = [0, 60, 79, 98, 116, 135, |
| | | 153, 172, 190, 209, 228, 246, 264, 283] |
| | | frame_count = 305 |
| | | output_dir = "E:/抖音-AI风景/新建文件夹\ProcessResults" |
| | | |
| | | cut_video_by_frames(target_file, selected_labels, frame_count, output_dir) |
| New file |
| | |
| | | import tkinter as tk |
| | | from tkinter import ttk |
| | | import vlc |
| | | import os |
| | | |
| | | |
| | | class VideoPlayer: |
| | | def __init__(self, root): |
| | | self.root = root |
| | | self.player = vlc.Instance() |
| | | self.is_playing = False |
| | | self.is_paused = False |
| | | self.listPlayer = None |
| | | self.create_ui() |
| | | |
| | | def create_ui(self): |
| | | # Canvas to display video |
| | | self.canvas = tk.Canvas(self.root, width=640, height=360) |
| | | self.canvas.pack() |
| | | |
| | | # Create play, pause, and stop buttons |
| | | self.play_button = tk.Button(self.root, text="Play", command=self.toggle_play_pause) |
| | | self.play_button.pack(side=tk.LEFT) |
| | | |
| | | self.stop_button = tk.Button(self.root, text="Stop", command=self.stop) |
| | | self.stop_button.pack(side=tk.LEFT) |
| | | |
| | | # Progress bar for video |
| | | self.progress_bar = ttk.Scale(self.root, from_=0, to=100, orient="horizontal", length=500, command=self.on_progress_drag) |
| | | self.progress_bar.pack() |
| | | |
| | | # Start a thread to update progress bar |
| | | self.update_progress_task() |
| | | |
| | | def add_video(self, video_path): |
| | | # Create media list and add video |
| | | self.mediaList = self.player.media_list_new() |
| | | self.media = self.player.media_new(video_path) |
| | | self.mediaList.add_media(self.media) |
| | | |
| | | # Create MediaListPlayer and associate it with the media list |
| | | self.listPlayer = self.player.media_list_player_new() |
| | | self.listPlayer.set_media_list(self.mediaList) |
| | | self.listPlayer.set_playback_mode(vlc.PlaybackMode(1)) |
| | | |
| | | def play(self): |
| | | if self.listPlayer: |
| | | self.listPlayer.play() |
| | | self.is_playing = True |
| | | self.is_paused = False |
| | | self.play_button.config(text="Pause") |
| | | |
| | | def stop(self): |
| | | if self.listPlayer: |
| | | self.listPlayer.stop() |
| | | self.is_playing = False |
| | | self.is_paused = False |
| | | self.play_button.config(text="Play") |
| | | |
| | | def toggle_play_pause(self): |
| | | if self.is_playing: |
| | | self.listPlayer.pause() |
| | | self.is_playing = False |
| | | self.is_paused = True |
| | | self.play_button.config(text="Resume") |
| | | elif self.is_paused: |
| | | self.listPlayer.play() |
| | | self.is_playing = True |
| | | self.is_paused = False |
| | | self.play_button.config(text="Pause") |
| | | else: |
| | | self.play() |
| | | |
| | | def update_progress_task(self): |
| | | # Continuously update the progress bar |
| | | if self.listPlayer: |
| | | current_time = self.listPlayer.get_media_player().get_time() # Get current playback time |
| | | duration = self.listPlayer.get_media_player().get_length() # Get video duration |
| | | if duration > 0: |
| | | progress = (current_time / duration) * 100 # Calculate progress percentage |
| | | self.progress_bar.set(progress) |
| | | |
| | | # Call the function every 100 ms |
| | | self.root.after(100, self.update_progress_task) |
| | | |
| | | def on_progress_drag(self, value): |
| | | # Seek the video to the dragged position |
| | | if self.listPlayer: |
| | | duration = self.listPlayer.get_media_player().get_length() |
| | | new_time = (float(value) / 100) * duration |
| | | self.listPlayer.get_media_player().set_time(int(new_time)) |
| | | |
| | | |
| | | def main(): |
| | | root = tk.Tk() |
| | | root.title("VLC Video Player") |
| | | |
| | | video_player = VideoPlayer(root) |
| | | video_path = "E:/抖音-AI风景/data/关注/1515593476095344/视频/7375164244729302272.mp4" # 替换为实际视频文件路径 |
| | | video_player.add_video(video_path) # Replace with the path to your video |
| | | # Start the Tkinter main loop |
| | | root.mainloop() |
| | | |
| | | |
| | | if __name__ == "__main__": |
| | | main() |
| New file |
| | |
| | | import tkinter as tk |
| | | from tkinter import filedialog, messagebox, ttk, Canvas,PhotoImage |
| | | import os |
| | | import numpy as np |
| | | from PIL import Image, ImageTk |
| | | import vlc |
| | | import subprocess |
| | | import functools |
| | | import cv2 |
| | | import threading |
| | | from multiprocessing import Pool |
| | | import time |
| | | from threading import Thread |
| | | from queue import Queue |
| | | |
| | | class LoopVlcPlayer: |
| | | def __init__(self, canvas): |
| | | self.player = vlc.Instance() |
| | | self.canvas = canvas |
| | | |
| | | # 创建媒体列表播放器和媒体播放器 |
| | | self.listPlayer = self.player.media_list_player_new() |
| | | self.media_player = self.player.media_player_new() |
| | | |
| | | # 绑定 media_player 到 Canvas |
| | | self.media_player.set_hwnd(self.canvas.winfo_id()) # For Windows |
| | | # 如果运行在 MacOS 或 Linux,请根据平台替换绑定方法: |
| | | # MacOS: self.media_player.set_nsobject(self.canvas.winfo_id()) |
| | | # Linux: self.media_player.set_xwindow(self.canvas.winfo_id()) |
| | | self.listPlayer.set_media_player(self.media_player) |
| | | |
| | | def addPlaylist(self, local_path): |
| | | if not os.path.exists(local_path): |
| | | raise FileNotFoundError(f"The file {local_path} does not exist.") |
| | | |
| | | self.media_list = self.player.media_list_new() |
| | | self.media_list.add_media(local_path) |
| | | self.listPlayer.set_media_list(self.media_list) |
| | | self.listPlayer.set_playback_mode(vlc.PlaybackMode.loop) |
| | | |
| | | self.play() |
| | | |
| | | def play(self): |
| | | # 启动播放 |
| | | if self.media_list is None: |
| | | raise ValueError("No media added to the playlist. Use add_playlist() first.") |
| | | self.listPlayer.play() |
| | | |
| | | def stop(self): |
| | | # 停止播放 |
| | | self.listPlayer.stop() |
| | | |
| | | def removePlaylist(self): |
| | | """ |
| | | 停止播放并释放媒体列表资源,允许文件在硬件上被其他程序删除。 |
| | | """ |
| | | if self.media_list: |
| | | self.stop() |
| | | self.media_list.release() |
| | | self.media_list = None # 释放引用以确保清理生效 |
| | | print("Playlist removed and resources released.") |
| | | |
| | | |
| | | # Global variables |
| | | video_files = [] # List of video files |
| | | current_video_idx = 0 # Index of the current video |
| | | current_video_path = "" |
| | | video_player = None |
| | | thumbnail_images = [] |
| | | split_info = [] |
| | | |
| | | # Create the main window |
| | | root = tk.Tk() |
| | | root.title("简易视频拆解工具 0.1") |
| | | root.state('zoomed') # Fullscreen |
| | | |
| | | screen_width = root.winfo_screenwidth() |
| | | screen_height = root.winfo_screenheight() |
| | | |
| | | |
| | | # Video playing frame |
| | | video_frame = tk.Frame(root, width=1080, height=1920) |
| | | video_frame.pack(side=tk.LEFT, padx=0) |
| | | |
| | | # Frame for thumbnail images |
| | | thumbnail_frame = tk.Frame(root, width=1080, height=1920) |
| | | thumbnail_frame.pack_forget() |
| | | |
| | | # Frame for right-side info and buttons |
| | | control_frame = tk.Frame(root, width=60, height=200, bg="lightblue") |
| | | control_frame.pack(side=tk.RIGHT, anchor="ne", padx=5, pady=5) |
| | | |
| | | |
| | | |
| | | selected_labels = [] # 记录当前选中的标签 |
| | | file_frames = [] |
| | | file_fps = 0 |
| | | file_frame_count = 0 |
| | | file_width =0 |
| | | file_height = 0 |
| | | |
| | | def display_thumbnails(video_file): |
| | | global selected_labels,file_frames,file_fps,file_frame_count,file_width,file_height |
| | | selected_labels = [] |
| | | file_frames = [] |
| | | file_fps = 0 |
| | | file_frame_count = 0 |
| | | file_width = 0 |
| | | file_height =0 |
| | | |
| | | row, col = 0, 0 |
| | | max_col = (screen_width - 100) // 90 - 1 # 动态地 |
| | | |
| | | # 创建Canvas和垂直滚动条 |
| | | thumbnail_frame.pack(side=tk.LEFT, padx=0) |
| | | |
| | | canvas = tk.Canvas(thumbnail_frame, width=screen_width - 100, height=screen_height) |
| | | canvas.grid(row=0, column=0, sticky='nsew') |
| | | |
| | | scrollbar = ttk.Scrollbar(thumbnail_frame, orient="vertical", command=canvas.yview) |
| | | scrollbar.grid(row=0, column=1, sticky='ns') |
| | | |
| | | canvas.configure(yscrollcommand=scrollbar.set) |
| | | |
| | | # 创建一个frame作为Canvas的子项来添加缩略图 |
| | | thumbnail_container = tk.Frame(canvas) |
| | | canvas.create_window((0, 0), window=thumbnail_container, anchor="nw") |
| | | |
| | | # 启用鼠标滚轮事件来滚动Canvas |
| | | def on_mouse_wheel(event): |
| | | """处理鼠标滚轮事件""" |
| | | canvas.yview_scroll(int(-1 * (event.delta / 120)), "units") |
| | | |
| | | # 绑定鼠标滚轮事件 |
| | | canvas.bind_all("<MouseWheel>", on_mouse_wheel) |
| | | |
| | | """ |
| | | 读取 MP4 视频并将每一帧存储到内存中 |
| | | """ |
| | | def convert_cv_to_tk_image(cv_image): |
| | | """ |
| | | 将 OpenCV 图像转换为 Tkinter 可用的图像格式 |
| | | """ |
| | | pil_image = Image.fromarray(cv2.cvtColor(cv_image, cv2.COLOR_BGR2RGB)) |
| | | # 转换为Tkinter可用的PhotoImage格式 |
| | | return ImageTk.PhotoImage(pil_image) |
| | | |
| | | def get_thumbnail_width_height(width,height,max_size=120): |
| | | scale = min(max_size / height, max_size / width) |
| | | return int(width * scale),int(height * scale) |
| | | |
| | | def add_label_to_contanier(idx,thumb,row,col): |
| | | thumb_label = tk.Label(thumbnail_container, image=thumb) |
| | | thumb_label.image = thumb # 必须保持对图像的引用 |
| | | thumb_label.grid(row=row, column=col, padx=1, pady=1) |
| | | |
| | | # 将toggle_checkmark的调用用 functools.partial包装,传递idx |
| | | thumb_label.bind("<Button-1>", functools.partial(toggle_checkmark, label=thumb_label, idx=idx)) |
| | | |
| | | checkmark_label = None |
| | | if idx == 0: # 默认第一个图片加对号 |
| | | checkmark_label = add_checkmark_icon(thumb_label) |
| | | selected_labels.append(idx) |
| | | # 将checkmark_label保存在标签中,后续点击时可以参考 |
| | | thumb_label.checkmark_label = checkmark_label |
| | | thumbnail_container.update() |
| | | thumbnail_container.update_idletasks() |
| | | |
| | | cap = cv2.VideoCapture(video_file) |
| | | file_frame_count = int(cap.get(cv2.CAP_PROP_FRAME_COUNT)) |
| | | file_width = int(cap.get(cv2.CAP_PROP_FRAME_WIDTH)) |
| | | file_height = int(cap.get(cv2.CAP_PROP_FRAME_HEIGHT)) |
| | | thumbnail_width,thumbnail_height = get_thumbnail_width_height(file_width,file_height) |
| | | file_fps = cap.get(cv2.CAP_PROP_FPS) |
| | | frame_idx = 0 |
| | | while True: |
| | | ret, frame = cap.read() |
| | | if not ret: |
| | | break |
| | | file_frames.append(frame) |
| | | add_label_to_contanier(frame_idx,convert_cv_to_tk_image(cv2.resize(frame, (thumbnail_width, thumbnail_height))),row,col) |
| | | frame_idx +=1 |
| | | col += 1 |
| | | if col >= max_col: |
| | | col = 0 |
| | | row += 1 |
| | | |
| | | cap.release() |
| | | # 更新Canvas的滚动区域 |
| | | thumbnail_container.update_idletasks() |
| | | canvas.config(scrollregion=canvas.bbox("all")) |
| | | |
| | | def toggle_checkmark(event, label, idx): |
| | | """切换勾选状态""" |
| | | checkmark_label = label.checkmark_label |
| | | # 如果已经有勾选图标,移除它;如果没有,添加它 |
| | | if checkmark_label: |
| | | # 移除勾选图标 |
| | | if idx == 0 : |
| | | return |
| | | checkmark_label.destroy() |
| | | label.checkmark_label = None |
| | | # 从选中的标签列表中移除 |
| | | if idx in selected_labels: |
| | | selected_labels.remove(idx) |
| | | else: |
| | | # 添加勾选图标 |
| | | if idx ==file_frame_count-1: |
| | | return |
| | | checkmark_label = add_checkmark_icon(label) |
| | | label.checkmark_label = checkmark_label |
| | | selected_labels.append(idx) |
| | | |
| | | #print(selected_labels) |
| | | |
| | | |
| | | def add_checkmark_icon(thumb_label): |
| | | """在图像上添加勾选图标""" |
| | | checkmark = Image.open("checkmark.png") # 假设你有一个勾选图标 |
| | | checkmark = checkmark.resize((30, 30)) # 调整图标大小 |
| | | checkmark_photo = ImageTk.PhotoImage(checkmark) |
| | | |
| | | # 创建一个标签来放置勾选图标 |
| | | checkmark_label = tk.Label(thumb_label, image=checkmark_photo, bg='white', width=30, height=30) |
| | | checkmark_label.image = checkmark_photo # 保持图标的引用 |
| | | # 将勾选图标放在缩略图右上角 |
| | | checkmark_label.place(x=80, y=5, anchor="ne") # 确保图标位置在右上角 |
| | | |
| | | return checkmark_label |
| | | |
| | | |
| | | def show_large_image(image): |
| | | # 创建一个新的窗口来显示大图 |
| | | top = tk.Toplevel(root) |
| | | top.title("Large Image") |
| | | |
| | | # 使用 PIL 创建 Image 对象 |
| | | image_pil = Image.fromarray(image) |
| | | image_tk = ImageTk.PhotoImage(image_pil) |
| | | |
| | | # 在新窗口中显示图片 |
| | | label = tk.Label(top, image=image_tk) |
| | | label.image = image_tk # 保持对图片的引用 |
| | | label.pack() |
| | | # 跳转到指定进度 |
| | | |
| | | |
| | | def on_progress_change(event): |
| | | global loopPlayer |
| | | total_time = loopPlayer.media_player.get_length() / 1000 # 视频总时长(秒) |
| | | new_time = (event.x / progress_bar.winfo_width()) * total_time # 计算新的时间 |
| | | loopPlayer.media_player.set_time(int(new_time * 1000)) # 设置新时间(毫秒) |
| | | |
| | | |
| | | |
| | | def use_video(): |
| | | loopPlayer.stop() |
| | | video_frame.pack_forget() |
| | | display_thumbnails(current_video_path) |
| | | |
| | | |
| | | def toggle_play_pause(): |
| | | global loopPlayer |
| | | if loopPlayer.listPlayer.is_playing(): |
| | | loopPlayer.listPlayer.pause() # 暂停播放 |
| | | play_pause_button.config(text="播放视频") # 更新按钮文本为播放 |
| | | else: |
| | | loopPlayer.listPlayer.play() # 播放 |
| | | play_pause_button.config(text="暂停视频") # 更新按钮文本为暂停 |
| | | |
| | | # 更新进度条并增加调试信息 |
| | | |
| | | |
| | | def update_progress(): |
| | | # 确保播放器正在播放 |
| | | global loopPlayer |
| | | if loopPlayer.media_player.is_playing(): |
| | | current_time = loopPlayer.media_player.get_time() / 1000 # 毫秒转为秒 |
| | | total_time = loopPlayer.media_player.get_length() / 1000 # 毫秒转为秒 |
| | | if total_time > 0: |
| | | progress_percentage = (current_time / total_time) * 100 |
| | | progress_var.set(progress_percentage) |
| | | progress_bar.after(10, update_progress) |
| | | else: |
| | | progress_bar.after(10, update_progress) |
| | | |
| | | |
| | | # 视频进度条 |
| | | progress_var = tk.DoubleVar() |
| | | progress_bar = ttk.Scale(video_frame, variable=progress_var, |
| | | from_=0, to=100, orient="horizontal", length=800) |
| | | progress_bar.pack(side=tk.TOP, pady=10) |
| | | |
| | | canvas = Canvas(video_frame, width=1080, height=1920) # 设置Canvas大小 |
| | | canvas.pack(side=tk.TOP) # Video player area under progress bar |
| | | |
| | | loopPlayer = LoopVlcPlayer(canvas) |
| | | |
| | | |
| | | |
| | | |
| | | # 绑定进度条点击事件,跳转到指定时间 |
| | | progress_bar.bind("<ButtonRelease-1>", on_progress_change) |
| | | |
| | | update_progress() |
| | | # 设置主窗口 |
| | | # Functions |
| | | |
| | | output_dir = "" |
| | | main_folder = "" |
| | | |
| | | def open_folder(): |
| | | global output_dir,main_folder |
| | | """Open folder and get all MP4 files""" |
| | | main_folder = filedialog.askdirectory() |
| | | if not main_folder: |
| | | return |
| | | |
| | | global video_files |
| | | video_files = [] |
| | | for root_dir, dirs, files in os.walk(main_folder): |
| | | for file in files: |
| | | if file.lower().endswith(".mp4"): |
| | | video_files.append(os.path.join(root_dir, file)) |
| | | |
| | | if video_files: |
| | | messagebox.showinfo("Info", f"文件数量:{len(video_files)}") |
| | | |
| | | root.title("文件夹:"+main_folder+ ",文件数量:"+str(len(video_files))) |
| | | root.update() # 刷新界面 |
| | | load_video(video_files[0]) |
| | | |
| | | output_dir = os.path.join(main_folder, "ProcessResults") |
| | | create_folders(output_dir) |
| | | else: |
| | | messagebox.showerror("Error", "没有找到MP4文件") |
| | | |
| | | |
| | | def load_video(video_path): |
| | | global loopPlayer |
| | | """Load video into the player and start playback""" |
| | | global current_video_path, video_player |
| | | update_title_add_file_path(video_path) |
| | | current_video_path = video_path |
| | | thumbnail_frame.pack_forget() |
| | | video_frame.pack(side=tk.LEFT, padx=0) |
| | | loopPlayer.addPlaylist(current_video_path) |
| | | loopPlayer.play() |
| | | |
| | | |
| | | |
| | | def delete_video(): |
| | | """Delete current video and play the next""" |
| | | global current_video_idx |
| | | loopPlayer.removePlaylist() |
| | | update_title_minus_one() |
| | | if current_video_idx < len(video_files): |
| | | os.remove(video_files[current_video_idx]) |
| | | current_video_idx += 1 |
| | | if current_video_idx < len(video_files): |
| | | load_video(video_files[current_video_idx]) |
| | | else: |
| | | messagebox.showinfo("Info", "处理完毕!") |
| | | |
| | | |
| | | |
| | | def save_video_slice(fps,width,height,frames_to_save, output_file): |
| | | """ |
| | | 根据指定的帧范围将视频保存为新的片段 |
| | | """ |
| | | fourcc = cv2.VideoWriter_fourcc(*'mp4v') # 用于生成 MP4 格式 |
| | | #cv2.VideoWriter_fourcc(*'H264') |
| | | #cv2.VideoWriter_fourcc(*'avc1') |
| | | #cv2.VideoWriter_fourcc(*'mp4v') |
| | | out = cv2.VideoWriter(output_file, fourcc, fps, (width, height)) |
| | | |
| | | # 遍历指定范围的帧并写入到新视频中 |
| | | for frame in frames_to_save: |
| | | out.write(frame) |
| | | out.release() |
| | | |
| | | def save_slice_in_thread(fps, width, height, frames, output_file): |
| | | save_video_slice(fps, width, height, frames, output_file) |
| | | |
| | | |
| | | def process_video_slices(save_hook,output_dir,file_basename): |
| | | """ |
| | | 并发处理多个视频切片并写入文件。 |
| | | |
| | | 参数: |
| | | - frames: 已加载的所有视频帧 |
| | | - slice_ranges: 每个切片的帧范围 [(start1, end1), (start2, end2), ...] |
| | | - output_dir: 输出视频切片保存的目录 |
| | | - fps: 帧率 |
| | | - width: 视频宽度 |
| | | - height: 视频高度 |
| | | """ |
| | | global file_frame_count,file_fps,file_width,file_height,selected_labels,file_frames |
| | | |
| | | def convert_to_ranges(frame_segments, total_frames): |
| | | ranges = [] |
| | | for i in range(len(frame_segments) - 1): |
| | | start_frame = frame_segments[i] |
| | | end_frame = frame_segments[i + 1] - 1 # 不包含end_frame |
| | | ranges.append([start_frame, end_frame]) |
| | | |
| | | # 处理最后一段 |
| | | last_start_frame = frame_segments[-1] |
| | | ranges.append([last_start_frame, total_frames]) # 最后一段直到视频结束 |
| | | return ranges |
| | | |
| | | slice_ranges = convert_to_ranges(selected_labels,file_frame_count) |
| | | |
| | | threads = [] |
| | | for i, slice_range in enumerate(slice_ranges): |
| | | # 设置输出路径 |
| | | if save_hook and i == 0: |
| | | output_file = os.path.join(os.path.join(output_dir, "hook"), f"{file_basename}.mp4") |
| | | else: |
| | | output_file = os.path.join(os.path.join(output_dir, "slice"), f"{file_basename}_{i + 1}.mp4") |
| | | |
| | | slice_frames = file_frames[slice_range[0]:slice_range[1]] |
| | | t = threading.Thread(target=save_slice_in_thread, args=(file_fps, file_width, file_height, slice_frames, output_file)) |
| | | threads.append(t) |
| | | t.start() |
| | | |
| | | # 可选:等待所有线程完成 |
| | | #for t in threads: |
| | | # t.join() |
| | | |
| | | |
| | | |
| | | |
| | | |
| | | # 创建线程池 |
| | | ''' |
| | | with ThreadPoolExecutor() as executor: |
| | | futures = [] |
| | | for i, slice_range in enumerate(slice_ranges): |
| | | # 设置输出路径 |
| | | if save_hook and i == 0: |
| | | output_file = os.path.join(os.path.join(output_dir, "hook"), f"{file_basename}_{i + 1}.mp4") |
| | | else: |
| | | output_file = os.path.join(os.path.join(output_dir, "slice"), f"{file_basename}_{i + 1}.mp4") |
| | | |
| | | # 提交任务到线程池 |
| | | futures.append(executor.submit(save_video_slice, slice_range, output_file)) |
| | | |
| | | # 等待所有任务完成 |
| | | for future in futures: |
| | | future.result() # 获取结果(如有异常会在这里抛出) |
| | | ''' |
| | | ''' |
| | | for i, slice_range in enumerate(slice_ranges): |
| | | if save_hook and i==0: |
| | | output_file = os.path.join(os.path.join(output_dir,"hook"), f"{file_basename}_{i+1}.mp4") |
| | | else: |
| | | output_file = os.path.join(os.path.join(output_dir,"slice"), f"{file_basename}_{i+1}.mp4") |
| | | save_video_slice(file_fps, file_width, file_height,file_frames[slice_range[0]:slice_range[1]], output_file) |
| | | ''' |
| | | |
| | | |
| | | |
| | | def split_video(): |
| | | """Delete current video and play the next""" |
| | | global current_video_idx |
| | | print(selected_labels) |
| | | update_title_minus_one() |
| | | if current_video_idx < len(video_files): |
| | | target_file = video_files[current_video_idx] |
| | | |
| | | save_hook_= save_hook.get() == 1 |
| | | save_music_= save_music.get() == 1 |
| | | save_music_without_hook_ = save_music_without_hook.get() == 1 |
| | | |
| | | cut_video_by_frames(save_hook_,save_music_,save_music_without_hook_,target_file,selected_labels,file_frame_count,output_dir) |
| | | |
| | | current_video_idx += 1 |
| | | if current_video_idx < len(video_files): |
| | | load_video(video_files[current_video_idx]) |
| | | else: |
| | | messagebox.showinfo("Info", "处理完成!") |
| | | thumbnail_frame.pack_forget() |
| | | |
| | | def even_split_video(): |
| | | """Delete current video and play the next""" |
| | | global current_video_idx,file_frame_count,output_dir |
| | | loopPlayer.stop() |
| | | update_title_minus_one() |
| | | if current_video_idx < len(video_files): |
| | | target_file = video_files[current_video_idx] |
| | | |
| | | even_split_count_= scale_progress_var.get() |
| | | scale_save_music_ = scale_save_music.get() == 1 |
| | | |
| | | even_cut_video_by_frames(even_split_count_,scale_save_music_,target_file,output_dir) |
| | | |
| | | current_video_idx += 1 |
| | | if current_video_idx < len(video_files): |
| | | load_video(video_files[current_video_idx]) |
| | | else: |
| | | messagebox.showinfo("Info", "处理完成!") |
| | | thumbnail_frame.pack_forget() |
| | | |
| | | |
| | | |
| | | def update_title_minus_one(): |
| | | root.title("文件夹:"+main_folder+ ",文件数量:"+str(len(video_files)-current_video_idx)) |
| | | root.update() |
| | | |
| | | def update_title_add_file_path(file_path): |
| | | root.title("文件夹:"+main_folder+ ",文件数量:<"+str(len(video_files)-current_video_idx)+">."+file_path) |
| | | root.update() |
| | | |
| | | def get_filename_without_extension(video_path): |
| | | # 获取文件名,去除路径 |
| | | filename_with_extension = os.path.basename(video_path) |
| | | # 去除扩展名 |
| | | filename_without_extension = os.path.splitext(filename_with_extension)[0] |
| | | return filename_without_extension |
| | | |
| | | def even_cut_video_by_frames(even_split_count_,scale_save_music_,video_path,output_dir): |
| | | """ |
| | | 将视频按照固定间隔进行平均切割。 |
| | | - output_dir: 输出视频文件的目录 |
| | | """ |
| | | global file_fps,file_frame_count,file_frames,file_width,file_height |
| | | |
| | | def split_video_into_slices(total_count, split_count_): |
| | | # 如果视频帧数小于等于分片数,则不拆分 |
| | | if total_count <= split_count_: |
| | | return [(0, total_count)] # 返回一个包含整个视频的切片 |
| | | # 计算需要的切片数量 |
| | | slice_count = total_count // split_count_ # 基本的切片数量 |
| | | remaining_frames = total_count % split_count_ # 剩余的帧数 |
| | | |
| | | slices = [] |
| | | # 创建每个切片 |
| | | for i in range(int(slice_count)): |
| | | start_frame = int(i * split_count_) |
| | | end_frame = int((i + 1) * split_count_) |
| | | slices.append((start_frame, end_frame)) |
| | | # 如果有剩余的帧,将其作为最后一个切片 |
| | | if remaining_frames > 0: |
| | | slices.append((int(slice_count * split_count_), int(slice_count * split_count_ + remaining_frames))) |
| | | return slices |
| | | |
| | | if file_frame_count ==0 : |
| | | cap = cv2.VideoCapture(video_path) |
| | | file_frame_count = int(cap.get(cv2.CAP_PROP_FRAME_COUNT)) |
| | | file_width = int(cap.get(cv2.CAP_PROP_FRAME_WIDTH)) |
| | | file_height = int(cap.get(cv2.CAP_PROP_FRAME_HEIGHT)) |
| | | file_fps = cap.get(cv2.CAP_PROP_FPS) |
| | | while True: |
| | | ret, frame = cap.read() |
| | | if not ret: |
| | | break |
| | | file_frames.append(frame) |
| | | cap.release() |
| | | |
| | | file_name_witout_extension = get_filename_without_extension(video_path) |
| | | if scale_save_music_: |
| | | output_file = os.path.join(os.path.join(output_dir,"music"), f"{file_name_witout_extension}.wav") |
| | | command = ["ffmpeg", "-i", video_path, "-vn", "-acodec", "pcm_s16le","-ar", "44100","-ac", "2", output_file] |
| | | print(" ".join(command)) |
| | | subprocess.Popen(command) |
| | | |
| | | slice_ranges = split_video_into_slices(file_frame_count,even_split_count_) |
| | | print("even_slice_ranges") |
| | | print(slice_ranges) |
| | | |
| | | threads = [] |
| | | for i, slice_range in enumerate(slice_ranges): |
| | | output_file = os.path.join(os.path.join(output_dir, "slice"), f"{file_name_witout_extension}_{i + 1}.mp4") |
| | | slice_frames = file_frames[slice_range[0]:slice_range[1]] |
| | | t = threading.Thread(target=save_slice_in_thread, args=(file_fps, file_width, file_height, slice_frames, output_file)) |
| | | threads.append(t) |
| | | t.start() |
| | | |
| | | # 可选:等待所有线程完成 |
| | | #for t in threads: |
| | | # t.join() |
| | | |
| | | |
| | | def cut_video_by_frames(save_hook,save_music,save_music_without_hook,video_path, frame_segments, total_frames, output_dir): |
| | | """ |
| | | 将视频按照指定的帧切片进行切割。 |
| | | |
| | | 参数: |
| | | - video_path: 输入视频的路径 |
| | | - frame_segments: 切片的帧序号数组,例如:[0, M1, M2, ..., Mlast] |
| | | - output_dir: 输出视频文件的目录 |
| | | """ |
| | | global file_fps |
| | | frame_segments.sort() |
| | | file_name_witout_extension = get_filename_without_extension(video_path) |
| | | |
| | | if save_music: |
| | | output_file = os.path.join(os.path.join(output_dir,"music"), f"{file_name_witout_extension}.wav") |
| | | if save_music_without_hook: |
| | | music_start_time = float(frame_segments[1]) / file_fps |
| | | command = ["ffmpeg", "-i", video_path, "-ss", str(music_start_time), "-vn", "-acodec", "pcm_s16le","-ar", "44100","-ac", "2", output_file] |
| | | print(" ".join(command)) |
| | | else: |
| | | command = ["ffmpeg", "-i", video_path, "-vn", "-acodec", "pcm_s16le","-ar", "44100","-ac", "2", output_file] |
| | | print(" ".join(command)) |
| | | subprocess.Popen(command) |
| | | if save_hook: |
| | | output_file = os.path.join(os.path.join(output_dir,"hook"), f"{file_name_witout_extension}.wav") |
| | | music_start_time = float(frame_segments[1]) / file_fps |
| | | command = ["ffmpeg", "-i", video_path, "-t", str(music_start_time), "-vn", "-acodec", "pcm_s16le", "-ar", "44100", "-ac", "2", output_file] |
| | | print(" ".join(command)) |
| | | subprocess.Popen(command) |
| | | |
| | | process_video_slices(save_hook,output_dir,file_name_witout_extension) |
| | | |
| | | |
| | | |
| | | |
| | | def create_folders(base_folder): |
| | | """Create a folder structure for saving results""" |
| | | hook_folder = os.path.join(base_folder, "hook") |
| | | music_folder = os.path.join(base_folder, "music") |
| | | category_folder = os.path.join(base_folder, "slice") |
| | | |
| | | if not os.path.exists(hook_folder): |
| | | os.makedirs(hook_folder) |
| | | if not os.path.exists(music_folder): |
| | | os.makedirs(music_folder) |
| | | if not os.path.exists(category_folder): |
| | | os.makedirs(category_folder) |
| | | |
| | | |
| | | # Buttons |
| | | open_folder_button = tk.Button(control_frame, text="打开文件夹", command=open_folder,width=30) |
| | | open_folder_button.pack(pady=5) |
| | | |
| | | # 播放控制按钮(播放/暂停) |
| | | play_pause_button = tk.Button(control_frame, text="暂停视频", command=toggle_play_pause,width=30) |
| | | play_pause_button.pack(pady=30) |
| | | |
| | | |
| | | delete_button = tk.Button(control_frame, text="保留", command=use_video,width=30,bg="green") |
| | | delete_button.pack(pady=10) |
| | | |
| | | delete_button = tk.Button(control_frame, text="不保留", command=delete_video,width=30,bg="red") |
| | | delete_button.pack() |
| | | |
| | | save_hook = tk.IntVar(value=1) # 默认勾选 |
| | | save_music = tk.IntVar(value=1) # 默认勾选 |
| | | save_music_without_hook = tk.IntVar(value=1) |
| | | |
| | | # 创建两个 Checkbutton |
| | | checkbox1 = tk.Checkbutton(control_frame, text="保存Hook视频", variable=save_hook,width=30) |
| | | checkbox1.pack(pady=30) |
| | | |
| | | checkbox2 = tk.Checkbutton(control_frame, text="保存音频", variable=save_music,width=30) |
| | | checkbox2.pack() |
| | | |
| | | checkbox3 = tk.Checkbutton(control_frame, text="音频(除hook音频)", variable=save_music_without_hook,width=30) |
| | | checkbox3.pack() |
| | | |
| | | |
| | | delete_button = tk.Button(control_frame, text="画面拆分", command=split_video,width=30) |
| | | delete_button.pack(pady=50) |
| | | |
| | | |
| | | def _on_scale_progress_change(value): |
| | | # 限制进度条的值为10、20、30、40、50 |
| | | rounded_value = round(float(value) / 5) * 5 |
| | | scale_progress_var.set(rounded_value) # 设置回进度条 |
| | | scale_progress_var = tk.DoubleVar() |
| | | scale_progress_bar = tk.Scale(control_frame, variable=scale_progress_var, from_=10, to=50, orient="horizontal", length=300, |
| | | tickinterval=10, showvalue=True, sliderlength=30) |
| | | scale_progress_bar.pack(pady=30) |
| | | scale_progress_bar.set(20) |
| | | |
| | | scale_save_music = tk.IntVar(value=1) # 默认勾选 |
| | | checkbox4 = tk.Checkbutton(control_frame, text="保存音频", variable=scale_save_music,width=30) |
| | | checkbox4.pack(pady=5) |
| | | |
| | | even_split_button = tk.Button(control_frame, text="平均拆分", command=even_split_video,width=30) |
| | | even_split_button.pack(pady=5) |
| | | |
| | | |
| | | ''' |
| | | menu_bar = tk.Menu(root) |
| | | file_menu = tk.Menu(menu_bar, tearoff=0) |
| | | file_menu.add_command(label="打开文件夹", command=open_folder) |
| | | menu_bar.add_cascade(label="文件", menu=file_menu) |
| | | root.config(menu=menu_bar) |
| | | ''' |
| | | |
| | | root.mainloop() |
| | |
| | | import os |
| | | import shutil |
| | | from PyInstaller.utils.hooks import collect_all |
| | | import zipfile |
| | | import time |
| | | |
| | | a = Analysis( |
| | | ['tkv.py'], |
| | | ['video-splitor.py'], |
| | | pathex=[], |
| | | binaries=[], |
| | | datas=[], |
| | |
| | | |
| | | # 定义额外拷贝的文件 |
| | | path_ffmpeg = os.path.join(os.getcwd(), 'ffmpeg.exe') |
| | | path_thumb = os.path.join(os.getcwd(), 'mp4_generate_thumbnails.bat') |
| | | path_checkmark = os.path.join(os.getcwd(), 'checkmark.png') |
| | | path_libvlc = os.path.join(os.getcwd(), 'libvlc.dll') |
| | | path_libvlccore = os.path.join(os.getcwd(), 'libvlccore.dll') |
| | | |
| | | plugins_path = os.path.join(os.getcwd(), 'plugins') |
| | | path_vlc_plugins = os.path.join(os.getcwd(), 'VLC.zip') |
| | | |
| | | # 将 ffmpeg.exe 和 plugins 文件夹添加到 EXE 的同级目录 |
| | | datas = [ |
| | | (path_ffmpeg, '.'), |
| | | (path_thumb, '.'), |
| | | (path_checkmark, '.'), |
| | | (path_libvlc, '.'), |
| | | (path_libvlccore, '.'), |
| | | (plugins_path, 'plugins') |
| | | (path_checkmark, '.'), |
| | | (path_vlc_plugins, '.') |
| | | ] |
| | | |
| | | |
| | |
| | | # 获取生成的 dist 目录 |
| | | dist_dir = os.path.join(os.getcwd(), 'dist') |
| | | |
| | | # 递归拷贝 plugins 目录 |
| | | if os.path.exists(plugins_path): |
| | | destination_plugins = os.path.join(dist_dir, 'plugins') |
| | | shutil.copytree(plugins_path, destination_plugins) |
| | | |
| | | if os.path.exists(path_ffmpeg): |
| | | shutil.copy(path_ffmpeg, dist_dir) |
| | | |
| | | if os.path.exists(path_thumb): |
| | | shutil.copy(path_thumb, dist_dir) |
| | | |
| | | if os.path.exists(path_checkmark): |
| | | shutil.copy(path_checkmark, dist_dir) |
| | | |
| | | if os.path.exists(path_libvlc): |
| | | shutil.copy(path_libvlc, dist_dir) |
| | | |
| | | if os.path.exists(path_libvlccore): |
| | | shutil.copy(path_libvlccore, dist_dir) |
| | | |
| | | if os.path.exists(path_vlc_plugins): |
| | | copied_file_path = shutil.copy(path_vlc_plugins, dist_dir) |
| | | with zipfile.ZipFile(copied_file_path, 'r') as zip_ref: |
| | | zip_ref.extractall(dist_dir) |
| | | time.sleep(1) # 等待资源释放 |
| | | os.remove(copied_file_path) |
| | | |
| | | # 在打包后的应用中执行文件拷贝操作 |
| | | post_processing_hook(exe) |
| | | |
| | | |
| | | post_processing_hook(exe) |