-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathv1.py
More file actions
269 lines (217 loc) · 10.9 KB
/
v1.py
File metadata and controls
269 lines (217 loc) · 10.9 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
import tkinter as tk
from tkinter import filedialog, messagebox, ttk
import os
from pathlib import Path
from PIL import Image
import threading
class ImageSizeReducer:
def __init__(self, root):
self.root = root
self.root.title("Image Size Reducer")
self.root.geometry("600x500")
self.root.resizable(False, False)
# Variables
self.input_folder = tk.StringVar()
self.output_folder = tk.StringVar()
self.quality_var = tk.IntVar(value=85) # Default quality
self.compression_var = tk.BooleanVar(value=False)
self.setup_ui()
def setup_ui(self):
# Main frame
main_frame = ttk.Frame(self.root, padding="20")
main_frame.grid(row=0, column=0, sticky=(tk.W, tk.E, tk.N, tk.S))
# Title
title_label = ttk.Label(main_frame, text="Image Size Reducer",
font=("Arial", 16, "bold"))
title_label.grid(row=0, column=0, columnspan=3, pady=(0, 20))
# Input folder selection
ttk.Label(main_frame, text="Input Folder:").grid(row=1, column=0, sticky=tk.W, pady=5)
ttk.Entry(main_frame, textvariable=self.input_folder, width=50).grid(row=1, column=1, pady=5, padx=5)
ttk.Button(main_frame, text="Browse", command=self.browse_input_folder).grid(row=1, column=2, pady=5)
# Output folder selection
ttk.Label(main_frame, text="Output Folder:").grid(row=2, column=0, sticky=tk.W, pady=5)
ttk.Entry(main_frame, textvariable=self.output_folder, width=50).grid(row=2, column=1, pady=5, padx=5)
ttk.Button(main_frame, text="Browse", command=self.browse_output_folder).grid(row=2, column=2, pady=5)
# Quality settings frame
quality_frame = ttk.LabelFrame(main_frame, text="Compression Settings", padding="10")
quality_frame.grid(row=3, column=0, columnspan=3, sticky=(tk.W, tk.E), pady=10)
# No compression option
ttk.Radiobutton(quality_frame, text="No Quality Loss (Optimize only)",
variable=self.compression_var, value=False).grid(row=0, column=0, sticky=tk.W, pady=5)
# With compression option
ttk.Radiobutton(quality_frame, text="Reduce Size with Quality Adjustment",
variable=self.compression_var, value=True).grid(row=1, column=0, sticky=tk.W, pady=5)
# Quality slider
ttk.Label(quality_frame, text="Quality Level:").grid(row=2, column=0, sticky=tk.W, pady=5)
quality_scale = ttk.Scale(quality_frame, from_=10, to=95, variable=self.quality_var,
orient=tk.HORIZONTAL, length=200)
quality_scale.grid(row=2, column=1, pady=5, padx=10)
self.quality_label = ttk.Label(quality_frame, text="85%")
self.quality_label.grid(row=2, column=2, pady=5)
# Bind quality slider update
quality_scale.configure(command=self.update_quality_label)
# Progress bar
self.progress = ttk.Progressbar(main_frame, mode='determinate')
self.progress.grid(row=4, column=0, columnspan=3, sticky=(tk.W, tk.E), pady=10)
# Status label
self.status_label = ttk.Label(main_frame, text="Ready")
self.status_label.grid(row=5, column=0, columnspan=3, pady=5)
# Buttons frame
button_frame = ttk.Frame(main_frame)
button_frame.grid(row=6, column=0, columnspan=3, pady=20)
ttk.Button(button_frame, text="Start Processing",
command=self.start_processing).pack(side=tk.LEFT, padx=10)
ttk.Button(button_frame, text="Clear",
command=self.clear_all).pack(side=tk.LEFT, padx=10)
ttk.Button(button_frame, text="Exit",
command=self.root.quit).pack(side=tk.LEFT, padx=10)
# Info text
info_text = """
How it works:
• No Quality Loss: Optimizes images without quality reduction (PNG optimization, JPEG without recompression)
• Reduce Size: Adjusts JPEG quality level to reduce file size
• Original resolution is maintained for both options
• Supported formats: JPG, JPEG, PNG, WEBP
"""
info_label = ttk.Label(main_frame, text=info_text, justify=tk.LEFT)
info_label.grid(row=7, column=0, columnspan=3, pady=10, sticky=tk.W)
def update_quality_label(self, value):
self.quality_label.config(text=f"{int(float(value))}%")
def browse_input_folder(self):
folder = filedialog.askdirectory(title="Select Input Folder")
if folder:
self.input_folder.set(folder)
# Auto-set output folder
if not self.output_folder.get():
self.output_folder.set(os.path.join(folder, "reduced_images"))
def browse_output_folder(self):
folder = filedialog.askdirectory(title="Select Output Folder")
if folder:
self.output_folder.set(folder)
def clear_all(self):
self.input_folder.set("")
self.output_folder.set("")
self.progress['value'] = 0
self.status_label.config(text="Ready")
def get_image_files(self, folder):
"""Get all image files from folder"""
extensions = {'.jpg', '.jpeg', '.png', '.webp', '.bmp', '.tiff'}
image_files = []
for file_path in Path(folder).iterdir():
if file_path.suffix.lower() in extensions and file_path.is_file():
image_files.append(file_path)
return image_files
def optimize_without_quality_loss(self, input_path, output_path):
"""Optimize image without quality loss"""
try:
with Image.open(input_path) as img:
# Convert to RGB if necessary
if img.mode in ('RGBA', 'LA', 'P'):
if img.mode == 'P':
img = img.convert('RGBA')
# Create white background for transparent images
background = Image.new('RGB', img.size, (255, 255, 255))
background.paste(img, mask=img.split()[-1] if img.mode == 'RGBA' else None)
img = background
elif img.mode != 'RGB':
img = img.convert('RGB')
# Save with optimization
if input_path.lower().endswith(('.png', '.bmp')):
# For PNG, use optimize flag
img.save(output_path, optimize=True)
else:
# For JPEG, save with high quality but progressive
img.save(output_path, quality=95, optimize=True, progressive=True)
return True
except Exception as e:
print(f"Error optimizing {input_path}: {e}")
return False
def compress_with_quality_reduction(self, input_path, output_path, quality):
"""Compress image with quality reduction"""
try:
with Image.open(input_path) as img:
# Convert to RGB if necessary
if img.mode in ('RGBA', 'LA', 'P'):
if img.mode == 'P':
img = img.convert('RGBA')
background = Image.new('RGB', img.size, (255, 255, 255))
background.paste(img, mask=img.split()[-1] if img.mode == 'RGBA' else None)
img = background
elif img.mode != 'RGB':
img = img.convert('RGB')
# Save with specified quality
img.save(output_path, quality=quality, optimize=True, progressive=True)
return True
except Exception as e:
print(f"Error compressing {input_path}: {e}")
return False
def process_images(self):
"""Main processing function"""
input_folder = self.input_folder.get()
output_folder = self.output_folder.get()
if not input_folder or not output_folder:
messagebox.showerror("Error", "Please select both input and output folders")
return
if not os.path.exists(input_folder):
messagebox.showerror("Error", "Input folder does not exist")
return
# Create output folder if it doesn't exist
os.makedirs(output_folder, exist_ok=True)
# Get image files
image_files = self.get_image_files(input_folder)
if not image_files:
messagebox.showinfo("No Images", "No image files found in the selected folder")
return
total_files = len(image_files)
processed = 0
success_count = 0
self.progress['maximum'] = total_files
self.progress['value'] = 0
for image_file in image_files:
filename = image_file.name
output_path = os.path.join(output_folder, filename)
self.status_label.config(text=f"Processing: {filename}")
try:
if self.compression_var.get():
# With quality reduction
success = self.compress_with_quality_reduction(
str(image_file), output_path, self.quality_var.get()
)
else:
# Without quality loss
success = self.optimize_without_quality_loss(str(image_file), output_path)
if success:
success_count += 1
# Get file sizes for comparison
original_size = os.path.getsize(str(image_file))
new_size = os.path.getsize(output_path)
# Log the result
reduction = ((original_size - new_size) / original_size) * 100
print(f"Processed: {filename}")
print(f" Original: {original_size/1024:.1f} KB")
print(f" New: {new_size/1024:.1f} KB")
print(f" Reduction: {reduction:.1f}%")
except Exception as e:
print(f"Failed to process {filename}: {e}")
processed += 1
self.progress['value'] = processed
self.root.update_idletasks()
# Show completion message
self.status_label.config(text="Processing Complete")
messagebox.showinfo(
"Complete",
f"Processed {total_files} images\n"
f"Successfully reduced: {success_count} images\n"
f"Output folder: {output_folder}"
)
def start_processing(self):
"""Start processing in a separate thread"""
thread = threading.Thread(target=self.process_images)
thread.daemon = True
thread.start()
def main():
root = tk.Tk()
app = ImageSizeReducer(root)
root.mainloop()
if __name__ == "__main__":
main()