Skip to content

Commit e720dc8

Browse files
committed
Merge branch 'master'
2 parents 09eec34 + 4a121bb commit e720dc8

File tree

10 files changed

+293
-0
lines changed

10 files changed

+293
-0
lines changed

FrameHarvestPro.exe

59.5 MB
Binary file not shown.

FrameHarvestPro.py

Lines changed: 293 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,293 @@
1+
# PRODUCT OF TRAKEXCEL AGENCY 2024. COPYRIGHT TERMS APPLIES
2+
import cv2
3+
import os
4+
import tkinter as tk
5+
from tkinter import ttk, filedialog, messagebox
6+
import subprocess
7+
import sys
8+
9+
10+
class VideoToFramesConverter:
11+
def __init__(self, root):
12+
self.root = root
13+
self.root.title("Video to Frames Pictures Converter")
14+
15+
# Set custom icon
16+
icon_path = 'vid.ico' # Replace with the path to your .ico file
17+
if getattr(sys, 'frozen', False): # Check if running as a PyInstaller executable
18+
icon_path = os.path.join(sys._MEIPASS, "vid.ico")
19+
20+
if os.path.exists(icon_path):
21+
self.root.iconbitmap(icon_path)
22+
23+
# Icon definitions
24+
self.browse_icon = tk.PhotoImage(file="folder.png" if not getattr(
25+
sys, 'frozen', False) else os.path.join(sys._MEIPASS, "folder.png"))
26+
self.cancel_icon = tk.PhotoImage(file="cancel.png" if not getattr(
27+
sys, 'frozen', False) else os.path.join(sys._MEIPASS, "cancel.png"))
28+
self.convert_icon = tk.PhotoImage(file="convert.png" if not getattr(
29+
sys, 'frozen', False) else os.path.join(sys._MEIPASS, "convert.png"))
30+
self.open_folder_icon = tk.PhotoImage(file="open-folder.png" if not getattr(
31+
sys, 'frozen', False) else os.path.join(sys._MEIPASS, "open-folder.png"))
32+
33+
self.create_widgets()
34+
self.cancel_conversion = False
35+
36+
def create_widgets(self):
37+
self.notebook = ttk.Notebook(self.root)
38+
self.notebook.grid(row=0, column=0, sticky="nsew", padx=10, pady=10)
39+
40+
# Page 1: Conversion functionality
41+
conversion_page = ttk.Frame(self.notebook)
42+
self.notebook.add(conversion_page, text="Convert Video")
43+
44+
self.create_conversion_widgets(conversion_page)
45+
46+
# Page 2: About the program
47+
about_page = ttk.Frame(self.notebook)
48+
self.notebook.add(about_page, text="About")
49+
50+
about_label = ttk.Label(
51+
about_page, text="Video to Frames Converter\nVersion 1.0\n© 2024 Trakexcel Agency-@Uzitrake")
52+
about_label.pack(padx=20, pady=14)
53+
54+
explanation_text = (
55+
"This program allows you to convert a video file into a sequence of frames.\n\n"
56+
"Usage:\n"
57+
"1. Select a video file using the 'Browse' button.\n"
58+
"2. Choose an output directory where the frames will be saved.\n"
59+
"3. Select the output format (WebP, JPEG, PNG) from the dropdown menu.\n"
60+
"4. Set the quality of the images (0-100).\n"
61+
"5. Click 'Convert' to start the conversion process.\n"
62+
"6. Optionally, you can cancel the conversion using the 'Cancel' button.\n"
63+
"7. Once the conversion is done, use 'Open in Explorer'."
64+
)
65+
66+
explanation_label = ttk.Label(
67+
about_page, text=explanation_text, anchor="w", justify="left")
68+
explanation_label.pack(padx=20, pady=20, fill="both")
69+
70+
# Allow window expansion
71+
self.root.grid_rowconfigure(0, weight=1)
72+
self.root.grid_columnconfigure(0, weight=1)
73+
74+
def create_conversion_widgets(self, parent):
75+
# Video file selection
76+
self.file_frame = ttk.Frame(parent, padding="10 5 10 5")
77+
self.file_frame.grid(row=0, column=0, sticky="ew")
78+
79+
self.file_path_label = ttk.Label(
80+
self.file_frame, text="Video To Convert:")
81+
self.file_path_label.grid(row=0, column=0, sticky="w")
82+
83+
self.file_path_entry = ttk.Entry(
84+
self.file_frame, state="disabled", width=40, font=("Helvetica", 10))
85+
self.file_path_entry.grid(row=0, column=1, padx=(0, 10), sticky="ew")
86+
87+
self.browse_button = ttk.Button(
88+
self.file_frame, text="Browse", command=self.browse_file)
89+
self.browse_button.grid(row=0, column=2)
90+
91+
# Output directory selection
92+
self.output_frame = ttk.Frame(parent, padding="10 5 10 5")
93+
self.output_frame.grid(row=1, column=0, sticky="ew")
94+
95+
self.output_path_label = ttk.Label(
96+
self.output_frame, text="Output Location:", font=("Helvetica", 10))
97+
self.output_path_label.grid(row=0, column=0, sticky="w")
98+
99+
self.output_path_entry = ttk.Entry(
100+
self.output_frame, state="disabled", width=40, font=("Helvetica", 10))
101+
self.output_path_entry.grid(row=0, column=1, padx=(0, 10), sticky="ew")
102+
103+
self.output_browse_button = ttk.Button(
104+
self.output_frame, text="Browse", command=self.browse_output_directory)
105+
self.output_browse_button.grid(row=0, column=2)
106+
107+
# Output format selection
108+
self.format_frame = ttk.Frame(parent, padding="10 5 10 5")
109+
self.format_frame.grid(row=2, column=0, sticky="ew")
110+
111+
self.format_label = ttk.Label(
112+
self.format_frame, text="Output Format:", font=("Helvetica", 10))
113+
self.format_label.grid(row=0, column=0, sticky="w")
114+
115+
self.format_var = tk.StringVar()
116+
self.format_var.set("WebP") # Default format
117+
self.format_dropdown = ttk.Combobox(self.format_frame, textvariable=self.format_var,
118+
values=["WebP", "JPEG", "PNG"])
119+
self.format_dropdown.grid(row=0, column=1, padx=(0, 10), sticky="ew")
120+
121+
# Quality selection
122+
self.quality_frame = ttk.Frame(parent, padding="10 5 10 5")
123+
self.quality_frame.grid(row=3, column=0, sticky="ew")
124+
125+
self.quality_label = ttk.Label(
126+
self.quality_frame, text="Quality (0-100):", font=("Helvetica", 10))
127+
self.quality_label.grid(row=0, column=0, sticky="w")
128+
129+
self.quality_var = tk.StringVar()
130+
self.quality_var.set("100")
131+
self.quality_spinbox = ttk.Spinbox(
132+
self.quality_frame, from_=0, to=100, textvariable=self.quality_var, width=7)
133+
self.quality_spinbox.grid(row=0, column=1, padx=(0, 10), sticky="ew")
134+
135+
# Button frame
136+
self.button_frame = ttk.Frame(parent, padding="10 5 10 5")
137+
self.button_frame.grid(row=4, column=0, sticky="ew")
138+
139+
# Convert button
140+
self.convert_button = ttk.Button(
141+
self.button_frame, text="Convert", command=self.start_conversion, image=self.convert_icon, compound="left")
142+
self.convert_button.grid(row=0, column=0, padx=5)
143+
144+
# Cancel button
145+
self.cancel_button = ttk.Button(
146+
self.button_frame, text="Cancel", command=self.cancel_conversion_process, image=self.cancel_icon, compound="left")
147+
self.cancel_button.grid(row=0, column=1, padx=5)
148+
149+
# Open in Explorer button
150+
self.open_folder_button = ttk.Button(self.button_frame, text="Open in Explorer",
151+
command=self.open_output_folder, image=self.open_folder_icon, compound="left")
152+
self.open_folder_button.grid(row=0, column=2, padx=5)
153+
154+
# Info label
155+
info_label = ttk.Label(
156+
parent, text="ℹ️ Check chosen directory for images", font="Helvetica 8 italic")
157+
info_label.grid(row=5, column=0, pady=5)
158+
159+
# Conversion status labels
160+
self.status_label = ttk.Label(parent, text="")
161+
self.status_label.grid(row=6, column=0, pady=5)
162+
163+
# Progress bar style
164+
style = ttk.Style(self.root)
165+
style.theme_use("clam")
166+
style.configure("green.Horizontal.TProgressbar",
167+
troughcolor='black', barcolor='green')
168+
169+
# Progress bar
170+
self.progress_var = tk.DoubleVar()
171+
self.progress_bar = ttk.Progressbar(
172+
parent, variable=self.progress_var, mode="determinate", style="green.Horizontal.TProgressbar")
173+
self.progress_bar.grid(row=7, column=0, sticky="ew", pady=5)
174+
175+
def browse_file(self):
176+
file_path = filedialog.askopenfilename(title="Select a video file", filetypes=[
177+
("Video Files", "*.mp4;*.avi")])
178+
self.file_path_entry.config(state="normal")
179+
self.file_path_entry.delete(0, tk.END)
180+
self.file_path_entry.insert(tk.END, file_path)
181+
self.file_path_entry.config(state="disabled")
182+
183+
def browse_output_directory(self):
184+
output_directory = filedialog.askdirectory(
185+
title="Select an output directory")
186+
self.output_path_entry.config(state="normal")
187+
self.output_path_entry.delete(0, tk.END)
188+
self.output_path_entry.insert(tk.END, output_directory)
189+
self.output_path_entry.config(state="disabled")
190+
191+
# Call open_output_folder to update the output_directory variable
192+
# self.open_output_folder()
193+
# CODE BY UZITRAKE
194+
195+
def start_conversion(self):
196+
self.cancel_conversion = False
197+
self.status_label.config(text="Converting...")
198+
self.progress_var.set(0) # Reset progress bar
199+
# Delayed call to start conversion
200+
self.root.after(100, self.convert_video_to_frames)
201+
202+
def cancel_conversion_process(self):
203+
self.cancel_conversion = True
204+
self.status_label.config(text="Conversion canceled.")
205+
self.progress_bar.stop()
206+
207+
def convert_video_to_frames(self):
208+
video_path = self.file_path_entry.get()
209+
output_directory = self.output_path_entry.get()
210+
quality = int(self.quality_var.get())
211+
output_format = self.format_var.get()
212+
213+
if not video_path:
214+
messagebox.showerror("Error", "Please select a video file.")
215+
self.reset_conversion_status()
216+
return
217+
218+
if not output_directory:
219+
messagebox.showerror("Error", "Please select an output directory.")
220+
self.reset_conversion_status()
221+
return
222+
223+
if not os.path.exists(output_directory):
224+
os.makedirs(output_directory)
225+
226+
video_capture = cv2.VideoCapture(video_path)
227+
total_frames = int(video_capture.get(cv2.CAP_PROP_FRAME_COUNT))
228+
229+
frame_count = 0
230+
231+
def process_frame():
232+
nonlocal frame_count
233+
234+
if not self.cancel_conversion:
235+
ret, frame = video_capture.read()
236+
if ret:
237+
frame_filename = os.path.join(output_directory, f'frame_{
238+
frame_count:04d}.{output_format.lower()}')
239+
240+
if output_format == "AVIF":
241+
imageio.imwrite(frame_filename, frame,
242+
format='AVIF', codec='av1')
243+
elif output_format == "PNG":
244+
cv2.imwrite(frame_filename, frame, [
245+
cv2.IMWRITE_PNG_COMPRESSION, quality])
246+
else:
247+
cv2.imwrite(frame_filename, frame, [
248+
cv2.IMWRITE_WEBP_QUALITY, quality] if output_format == "WebP" else [])
249+
250+
frame_count += 1
251+
252+
# Update progress bar
253+
progress_value = (frame_count + 1) / total_frames * 100
254+
self.progress_var.set(progress_value)
255+
self.root.update_idletasks() # Force update of the GUI
256+
257+
# Schedule the next frame processing
258+
self.root.after(10, process_frame)
259+
else:
260+
# Release resources and finalize
261+
video_capture.release()
262+
cv2.destroyAllWindows()
263+
264+
if self.cancel_conversion:
265+
self.status_label.config(text="Conversion canceled.")
266+
else:
267+
self.status_label.config(text="Conversion completed.")
268+
self.progress_bar.stop()
269+
self.reset_conversion_status()
270+
271+
process_frame()
272+
273+
def open_output_folder(self):
274+
output_directory = self.output_path_entry.get()
275+
276+
if output_directory and os.path.exists(output_directory):
277+
os.startfile(output_directory)
278+
else:
279+
messagebox.showwarning(
280+
"Warning", "Output directory does not exist.")
281+
282+
def reset_conversion_status(self):
283+
self.cancel_conversion = False
284+
self.status_label.config(text="")
285+
self.progress_bar.stop()
286+
287+
288+
if __name__ == "__main__":
289+
root = tk.Tk()
290+
style = ttk.Style(root)
291+
style.theme_use("clam")
292+
converter = VideoToFramesConverter(root)
293+
root.mainloop()

cancel.png

716 Bytes
Loading

convert.png

812 Bytes
Loading

folder.png

441 Bytes
Loading

open-folder.png

375 Bytes
Loading

vid-main.ico

4.19 KB
Binary file not shown.

vid-main.png

589 Bytes
Loading

vid.ico

1.12 KB
Binary file not shown.

vid.png

405 Bytes
Loading

0 commit comments

Comments
 (0)