Spaces:
Runtime error
Runtime error
| #!/usr/bin/env python3 | |
| """ | |
| DR-Image-Magic - FULL FEATURED GUI | |
| All features, no database needed! | |
| """ | |
| import tkinter as tk | |
| from tkinter import filedialog, messagebox, ttk, scrolledtext | |
| from PIL import Image, ImageTk, ImageEnhance, ImageFilter, ImageOps, ImageDraw, ImageFont | |
| import os | |
| from pathlib import Path | |
| import numpy as np | |
| from datetime import datetime | |
| class DrImageMagicFull: | |
| def __init__(self, root): | |
| self.root = root | |
| self.root.title("DR-Image-Magic - FULL EDITION") | |
| self.root.geometry("1200x900") | |
| self.root.configure(bg='#0a0a0a') | |
| self.current_image = None | |
| self.original_image = None | |
| self.image_path = None | |
| self.history = [] # Undo history | |
| self.setup_ui() | |
| def setup_ui(self): | |
| # Main container with scrollbar | |
| main_canvas = tk.Canvas(self.root, bg='#0a0a0a', highlightthickness=0) | |
| scrollbar = ttk.Scrollbar(self.root, orient="vertical", command=main_canvas.yview) | |
| scrollable_frame = tk.Frame(main_canvas, bg='#0a0a0a') | |
| scrollable_frame.bind( | |
| "<Configure>", | |
| lambda e: main_canvas.configure(scrollregion=main_canvas.bbox("all")) | |
| ) | |
| main_canvas.create_window((0, 0), window=scrollable_frame, anchor="nw") | |
| main_canvas.configure(yscrollcommand=scrollbar.set) | |
| main_canvas.pack(side="left", fill="both", expand=True) | |
| scrollbar.pack(side="right", fill="y") | |
| # Enable mousewheel scrolling | |
| def _on_mousewheel(event): | |
| main_canvas.yview_scroll(int(-1*(event.delta/120)), "units") | |
| main_canvas.bind_all("<MouseWheel>", _on_mousewheel) | |
| # Header | |
| header = tk.Label( | |
| scrollable_frame, | |
| text="🎨 DR-IMAGE-MAGIC", | |
| font=("Arial", 32, "bold"), | |
| bg='#0a0a0a', | |
| fg='#ff6600' | |
| ) | |
| header.pack(pady=15) | |
| subtitle = tk.Label( | |
| scrollable_frame, | |
| text="FULL FEATURED EDITION - All Tools, No Limits", | |
| font=("Arial", 12), | |
| bg='#0a0a0a', | |
| fg='#888' | |
| ) | |
| subtitle.pack() | |
| # Upload Section | |
| upload_frame = tk.Frame(scrollable_frame, bg='#0a0a0a') | |
| upload_frame.pack(pady=15) | |
| upload_btn = tk.Button( | |
| upload_frame, | |
| text="📁 UPLOAD IMAGE", | |
| command=self.upload_image, | |
| font=("Arial", 16, "bold"), | |
| bg='#ff6600', | |
| fg='white', | |
| padx=30, | |
| pady=12, | |
| cursor='hand2', | |
| relief=tk.FLAT | |
| ) | |
| upload_btn.pack() | |
| # Image Preview | |
| preview_frame = tk.Frame(scrollable_frame, bg='#1a1a1a', relief=tk.SUNKEN, bd=2) | |
| preview_frame.pack(pady=10, padx=20, fill=tk.BOTH) | |
| self.image_label = tk.Label( | |
| preview_frame, | |
| bg='#1a1a1a', | |
| text="No Image Loaded\n\nDrag & Drop or Click Upload", | |
| fg='#666', | |
| font=("Arial", 14) | |
| ) | |
| self.image_label.pack(pady=40, padx=20) | |
| # Quick Actions | |
| quick_frame = tk.Frame(scrollable_frame, bg='#0a0a0a') | |
| quick_frame.pack(pady=10) | |
| quick_btns = [ | |
| ("🔄 UNDO", self.undo_last, '#ff9900'), | |
| ("↺ RESET", self.reset_image, '#cc0000'), | |
| ("💾 SAVE", self.save_image, '#00cc00'), | |
| ("📦 BATCH SAVE", self.batch_save, '#0066cc'), | |
| ] | |
| for text, cmd, color in quick_btns: | |
| btn = tk.Button( | |
| quick_frame, | |
| text=text, | |
| command=cmd, | |
| font=("Arial", 11, "bold"), | |
| bg=color, | |
| fg='white', | |
| padx=15, | |
| pady=8, | |
| cursor='hand2', | |
| relief=tk.FLAT | |
| ) | |
| btn.pack(side=tk.LEFT, padx=5) | |
| # EFFECTS SECTIONS | |
| self.create_effects_section(scrollable_frame) | |
| def create_effects_section(self, parent): | |
| # Section 1: ESSENTIAL ENHANCEMENTS | |
| self.create_section(parent, "⚡ ESSENTIAL ENHANCEMENTS", [ | |
| ("✨ Auto Enhance", self.auto_enhance, "Smart enhancement"), | |
| ("💎 Super Sharpen", self.super_sharpen, "Crystal clear"), | |
| ("🌈 Color Pop", self.color_pop, "Vibrant colors"), | |
| ("🔥 HDR Effect", self.hdr_effect, "High dynamic range"), | |
| ]) | |
| # Section 2: ARTISTIC STYLES | |
| self.create_section(parent, "🎨 ARTISTIC STYLES", [ | |
| ("🖼️ Oil Painting", self.oil_painting, "Classic art"), | |
| ("📸 Film Noir", self.film_noir, "Black & white drama"), | |
| ("🌅 Golden Hour", self.golden_hour, "Warm sunset glow"), | |
| ("❄️ Ice Cold", self.ice_cold, "Cool blue tones"), | |
| ("🔴 Infrared", self.infrared, "IR photography"), | |
| ("⚫ High Key B&W", self.high_key_bw, "Bright grayscale"), | |
| ]) | |
| # Section 3: DRAMATIC EFFECTS | |
| self.create_section(parent, "💥 DRAMATIC EFFECTS", [ | |
| ("🌙 Dark Fantasy", self.dark_fantasy, "Gothic mood"), | |
| ("☀️ Sunburst", self.sunburst, "Intense brightness"), | |
| ("🎭 Vignette Drama", self.vignette_drama, "Dark edges"), | |
| ("✨ Glow", self.glow_effect, "Soft luminous"), | |
| ("🔆 Cross Process", self.cross_process, "Film effect"), | |
| ]) | |
| # Section 4: VINTAGE & RETRO | |
| self.create_section(parent, "📷 VINTAGE & RETRO", [ | |
| ("📼 VHS Glitch", self.vhs_glitch, "80s video"), | |
| ("📺 CRT Monitor", self.crt_effect, "Old screen"), | |
| ("🎞️ 70s Film", self.seventies_film, "Retro warm"), | |
| ("📟 Polaroid", self.polaroid, "Instant camera"), | |
| ("🌄 Faded Memory", self.faded_memory, "Old photo"), | |
| ]) | |
| # Section 5: MODERN & DIGITAL | |
| self.create_section(parent, "🚀 MODERN & DIGITAL", [ | |
| ("💻 Cyberpunk", self.cyberpunk, "Neon future"), | |
| ("🌐 Glitch Art", self.glitch_art, "Digital chaos"), | |
| ("🎮 Pixel Art", self.pixel_art, "8-bit style"), | |
| ("🌌 Vaporwave", self.vaporwave, "A E S T H E T I C"), | |
| ("⚡ Neon Lights", self.neon_lights, "Bright neon"), | |
| ]) | |
| # Section 6: PRO PRESETS | |
| self.create_section(parent, "🏆 PRO PRESETS", [ | |
| ("📱 Instagram Pro", self.instagram_pro, "Social ready"), | |
| ("🖼️ Gallery Print", self.gallery_print, "Museum quality"), | |
| ("💼 Professional", self.professional, "Business look"), | |
| ("🎪 Artistic Bold", self.artistic_bold, "Creative statement"), | |
| ("🕯️ Candlelight Sketch", self.candlelight_sketch, "Pencil + warm glow"), | |
| ]) | |
| # Section 7: IMAGE TOOLS | |
| self.create_section(parent, "🔧 IMAGE TOOLS", [ | |
| ("🔲 Expand Canvas", self.expand_canvas, "Extend image"), | |
| ("📐 Upscale 2x", self.upscale_image, "Make bigger"), | |
| ("🎨 Style Transfer", self.style_transfer, "Apply style"), | |
| ("🔄 Rotate 90°", self.rotate_90, "Quick rotate"), | |
| ("↔️ Flip Horizontal", self.flip_horizontal, "Mirror"), | |
| ("↕️ Flip Vertical", self.flip_vertical, "Flip"), | |
| ]) | |
| def create_section(self, parent, title, effects): | |
| section = tk.LabelFrame( | |
| parent, | |
| text=title, | |
| font=("Arial", 14, "bold"), | |
| bg='#0a0a0a', | |
| fg='#ff6600', | |
| relief=tk.GROOVE, | |
| bd=2 | |
| ) | |
| section.pack(pady=15, padx=20, fill=tk.X) | |
| grid_frame = tk.Frame(section, bg='#0a0a0a') | |
| grid_frame.pack(pady=10, padx=10) | |
| row, col = 0, 0 | |
| for name, cmd, desc in effects: | |
| btn_frame = tk.Frame(grid_frame, bg='#1a1a1a', relief=tk.RAISED, bd=1) | |
| btn_frame.grid(row=row, column=col, padx=5, pady=5, sticky='ew') | |
| btn = tk.Button( | |
| btn_frame, | |
| text=name, | |
| command=cmd, | |
| font=("Arial", 10, "bold"), | |
| bg='#1a1a1a', | |
| fg='#ff6600', | |
| padx=12, | |
| pady=8, | |
| cursor='hand2', | |
| relief=tk.FLAT | |
| ) | |
| btn.pack(fill=tk.X) | |
| desc_label = tk.Label( | |
| btn_frame, | |
| text=desc, | |
| font=("Arial", 8), | |
| bg='#1a1a1a', | |
| fg='#666' | |
| ) | |
| desc_label.pack() | |
| col += 1 | |
| if col > 3: # 4 columns | |
| col = 0 | |
| row += 1 | |
| def upload_image(self): | |
| file_path = filedialog.askopenfilename( | |
| title="Select Image", | |
| filetypes=[("Images", "*.png *.jpg *.jpeg *.webp *.bmp"), ("All", "*.*")] | |
| ) | |
| if file_path: | |
| try: | |
| self.image_path = file_path | |
| self.original_image = Image.open(file_path).convert('RGB') | |
| self.current_image = self.original_image.copy() | |
| self.history = [self.current_image.copy()] | |
| self.display_image(self.current_image) | |
| except Exception as e: | |
| messagebox.showerror("Error", f"Failed to load: {e}") | |
| def display_image(self, image): | |
| display_img = image.copy() | |
| display_img.thumbnail((600, 400), Image.Resampling.LANCZOS) | |
| photo = ImageTk.PhotoImage(display_img) | |
| self.image_label.configure(image=photo, text="") | |
| self.image_label.image = photo | |
| def save_to_history(self): | |
| if self.current_image: | |
| self.history.append(self.current_image.copy()) | |
| if len(self.history) > 20: # Keep last 20 | |
| self.history.pop(0) | |
| def check_image(self): | |
| if not self.current_image: | |
| messagebox.showwarning("No Image", "Upload an image first!") | |
| return False | |
| return True | |
| def apply_effect(self, effect_func): | |
| if not self.check_image(): return | |
| self.save_to_history() | |
| try: | |
| self.current_image = effect_func(self.current_image) | |
| self.display_image(self.current_image) | |
| except Exception as e: | |
| messagebox.showerror("Effect Error", str(e)) | |
| # === ESSENTIAL ENHANCEMENTS === | |
| def auto_enhance(self): | |
| if not self.check_image(): return | |
| self.save_to_history() | |
| img = self.current_image | |
| img = ImageEnhance.Contrast(img).enhance(1.2) | |
| img = ImageEnhance.Sharpness(img).enhance(1.3) | |
| img = ImageEnhance.Color(img).enhance(1.1) | |
| self.current_image = img | |
| self.display_image(img) | |
| def super_sharpen(self): | |
| self.apply_effect(lambda img: img.filter(ImageFilter.UnsharpMask(radius=2, percent=150, threshold=3))) | |
| def color_pop(self): | |
| self.apply_effect(lambda img: ImageEnhance.Color(img).enhance(1.8)) | |
| def hdr_effect(self): | |
| if not self.check_image(): return | |
| self.save_to_history() | |
| img = self.current_image | |
| img = ImageEnhance.Contrast(img).enhance(1.5) | |
| img = ImageEnhance.Brightness(img).enhance(0.95) | |
| img = ImageEnhance.Color(img).enhance(1.3) | |
| self.current_image = img | |
| self.display_image(img) | |
| # === ARTISTIC STYLES === | |
| def oil_painting(self): | |
| self.apply_effect(lambda img: img.filter(ImageFilter.SMOOTH_MORE).filter(ImageFilter.EDGE_ENHANCE)) | |
| def film_noir(self): | |
| if not self.check_image(): return | |
| self.save_to_history() | |
| img = self.current_image.convert('L').convert('RGB') | |
| img = ImageEnhance.Contrast(img).enhance(1.6) | |
| self.current_image = img | |
| self.display_image(img) | |
| def golden_hour(self): | |
| if not self.check_image(): return | |
| self.save_to_history() | |
| img = self.current_image | |
| r, g, b = img.split() | |
| r = r.point(lambda i: min(255, int(i * 1.15))) | |
| g = g.point(lambda i: min(255, int(i * 1.08))) | |
| b = b.point(lambda i: int(i * 0.88)) | |
| img = Image.merge('RGB', (r, g, b)) | |
| img = ImageEnhance.Brightness(img).enhance(1.05) | |
| self.current_image = img | |
| self.display_image(img) | |
| def ice_cold(self): | |
| if not self.check_image(): return | |
| self.save_to_history() | |
| img = self.current_image | |
| r, g, b = img.split() | |
| r = r.point(lambda i: int(i * 0.85)) | |
| g = g.point(lambda i: int(i * 0.92)) | |
| b = b.point(lambda i: min(255, int(i * 1.15))) | |
| self.current_image = Image.merge('RGB', (r, g, b)) | |
| self.display_image(self.current_image) | |
| def infrared(self): | |
| if not self.check_image(): return | |
| self.save_to_history() | |
| img = self.current_image.convert('L') | |
| img = ImageOps.invert(img) | |
| img = ImageEnhance.Contrast(img).enhance(1.3) | |
| r = img | |
| g = img | |
| b = img.point(lambda i: int(i * 0.7)) | |
| self.current_image = Image.merge('RGB', (r, g, b)) | |
| self.display_image(self.current_image) | |
| def high_key_bw(self): | |
| if not self.check_image(): return | |
| self.save_to_history() | |
| img = self.current_image.convert('L').convert('RGB') | |
| img = ImageEnhance.Brightness(img).enhance(1.2) | |
| img = ImageEnhance.Contrast(img).enhance(0.8) | |
| self.current_image = img | |
| self.display_image(img) | |
| # === DRAMATIC EFFECTS === | |
| def dark_fantasy(self): | |
| if not self.check_image(): return | |
| self.save_to_history() | |
| img = self.current_image | |
| img = ImageEnhance.Brightness(img).enhance(0.6) | |
| img = ImageEnhance.Contrast(img).enhance(1.6) | |
| img = ImageEnhance.Color(img).enhance(0.8) | |
| self.current_image = img | |
| self.display_image(img) | |
| def sunburst(self): | |
| self.apply_effect(lambda img: ImageEnhance.Brightness(img).enhance(1.4)) | |
| def vignette_drama(self): | |
| if not self.check_image(): return | |
| self.save_to_history() | |
| img = self.current_image | |
| width, height = img.size | |
| mask = Image.new('L', (width, height), 0) | |
| draw = ImageDraw.Draw(mask) | |
| for i in range(min(width, height) // 4): | |
| alpha = int(255 * (i / (min(width, height) // 4))) | |
| draw.ellipse( | |
| [i, i, width-i, height-i], | |
| fill=alpha | |
| ) | |
| dark = Image.new('RGB', img.size, (0, 0, 0)) | |
| self.current_image = Image.composite(img, dark, mask) | |
| self.display_image(self.current_image) | |
| def glow_effect(self): | |
| if not self.check_image(): return | |
| self.save_to_history() | |
| img = self.current_image | |
| glow = img.filter(ImageFilter.GaussianBlur(15)) | |
| glow = ImageEnhance.Brightness(glow).enhance(1.5) | |
| self.current_image = Image.blend(img, glow, 0.3) | |
| self.display_image(self.current_image) | |
| def cross_process(self): | |
| if not self.check_image(): return | |
| self.save_to_history() | |
| img = self.current_image | |
| r, g, b = img.split() | |
| r = r.point(lambda i: min(255, int(i * 1.1))) | |
| g = g.point(lambda i: int(i * 0.95)) | |
| b = b.point(lambda i: min(255, int(i * 1.15))) | |
| img = Image.merge('RGB', (r, g, b)) | |
| img = ImageEnhance.Contrast(img).enhance(1.3) | |
| self.current_image = img | |
| self.display_image(img) | |
| # === VINTAGE & RETRO === | |
| def vhs_glitch(self): | |
| if not self.check_image(): return | |
| self.save_to_history() | |
| img = self.current_image | |
| img = ImageEnhance.Contrast(img).enhance(1.2) | |
| img = ImageEnhance.Color(img).enhance(0.8) | |
| self.current_image = img | |
| self.display_image(img) | |
| def crt_effect(self): | |
| self.apply_effect(lambda img: ImageEnhance.Brightness(img).enhance(1.1)) | |
| def seventies_film(self): | |
| if not self.check_image(): return | |
| self.save_to_history() | |
| img = self.current_image | |
| r, g, b = img.split() | |
| r = r.point(lambda i: min(255, int(i * 1.12))) | |
| g = g.point(lambda i: min(255, int(i * 1.05))) | |
| b = b.point(lambda i: int(i * 0.9)) | |
| img = Image.merge('RGB', (r, g, b)) | |
| img = ImageEnhance.Contrast(img).enhance(0.9) | |
| self.current_image = img | |
| self.display_image(img) | |
| def polaroid(self): | |
| if not self.check_image(): return | |
| self.save_to_history() | |
| img = self.current_image | |
| img = ImageEnhance.Brightness(img).enhance(1.1) | |
| img = ImageEnhance.Color(img).enhance(0.85) | |
| img = ImageEnhance.Contrast(img).enhance(0.95) | |
| self.current_image = img | |
| self.display_image(img) | |
| def faded_memory(self): | |
| if not self.check_image(): return | |
| self.save_to_history() | |
| img = self.current_image | |
| img = ImageEnhance.Color(img).enhance(0.6) | |
| img = ImageEnhance.Brightness(img).enhance(1.15) | |
| img = ImageEnhance.Contrast(img).enhance(0.8) | |
| self.current_image = img | |
| self.display_image(img) | |
| # === MODERN & DIGITAL === | |
| def cyberpunk(self): | |
| if not self.check_image(): return | |
| self.save_to_history() | |
| img = self.current_image | |
| r, g, b = img.split() | |
| r = r.point(lambda i: min(255, int(i * 1.2))) | |
| g = g.point(lambda i: int(i * 0.9)) | |
| b = b.point(lambda i: min(255, int(i * 1.3))) | |
| img = Image.merge('RGB', (r, g, b)) | |
| img = ImageEnhance.Contrast(img).enhance(1.4) | |
| self.current_image = img | |
| self.display_image(img) | |
| def glitch_art(self): | |
| self.apply_effect(lambda img: img.filter(ImageFilter.EDGE_ENHANCE_MORE)) | |
| def pixel_art(self): | |
| if not self.check_image(): return | |
| self.save_to_history() | |
| img = self.current_image | |
| small = img.resize((img.width // 16, img.height // 16), Image.Resampling.NEAREST) | |
| self.current_image = small.resize(img.size, Image.Resampling.NEAREST) | |
| self.display_image(self.current_image) | |
| def vaporwave(self): | |
| if not self.check_image(): return | |
| self.save_to_history() | |
| img = self.current_image | |
| r, g, b = img.split() | |
| r = r.point(lambda i: min(255, int(i * 1.2))) | |
| g = g.point(lambda i: min(255, int(i * 0.95))) | |
| b = b.point(lambda i: min(255, int(i * 1.25))) | |
| img = Image.merge('RGB', (r, g, b)) | |
| img = ImageEnhance.Color(img).enhance(1.5) | |
| self.current_image = img | |
| self.display_image(img) | |
| def neon_lights(self): | |
| if not self.check_image(): return | |
| self.save_to_history() | |
| img = self.current_image | |
| img = ImageEnhance.Color(img).enhance(2.0) | |
| img = ImageEnhance.Contrast(img).enhance(1.3) | |
| img = ImageEnhance.Brightness(img).enhance(1.2) | |
| self.current_image = img | |
| self.display_image(img) | |
| # === PRO PRESETS === | |
| def instagram_pro(self): | |
| if not self.check_image(): return | |
| self.save_to_history() | |
| img = self.current_image | |
| img = ImageEnhance.Contrast(img).enhance(1.15) | |
| img = ImageEnhance.Color(img).enhance(1.2) | |
| img = ImageEnhance.Sharpness(img).enhance(1.1) | |
| self.current_image = img | |
| self.display_image(img) | |
| def gallery_print(self): | |
| if not self.check_image(): return | |
| self.save_to_history() | |
| img = self.current_image | |
| img = ImageEnhance.Contrast(img).enhance(1.1) | |
| img = ImageEnhance.Sharpness(img).enhance(1.3) | |
| img = ImageEnhance.Color(img).enhance(1.05) | |
| self.current_image = img | |
| self.display_image(img) | |
| def professional(self): | |
| self.apply_effect(lambda img: ImageEnhance.Contrast(img).enhance(1.1)) | |
| def artistic_bold(self): | |
| if not self.check_image(): return | |
| self.save_to_history() | |
| img = self.current_image | |
| img = ImageEnhance.Color(img).enhance(1.6) | |
| img = ImageEnhance.Contrast(img).enhance(1.4) | |
| self.current_image = img | |
| self.display_image(img) | |
| def candlelight_sketch(self): | |
| """Pencil sketch with warm candlelight - mimics the style Dave showed""" | |
| if not self.check_image(): return | |
| self.save_to_history() | |
| img = self.current_image | |
| # Step 1: Reduce color saturation (pencil sketch base) | |
| img = ImageEnhance.Color(img).enhance(0.3) | |
| # Step 2: Add warm candlelight glow (orange/golden tones) | |
| r, g, b = img.split() | |
| # Boost reds/oranges (candlelight) | |
| r = r.point(lambda i: min(255, int(i * 1.3))) | |
| g = g.point(lambda i: min(255, int(i * 1.15))) | |
| b = b.point(lambda i: int(i * 0.75)) # Reduce blue for warmth | |
| img = Image.merge('RGB', (r, g, b)) | |
| # Step 3: High contrast (dramatic lighting) | |
| img = ImageEnhance.Contrast(img).enhance(1.8) | |
| # Step 4: Darken overall (dark shadows) | |
| img = ImageEnhance.Brightness(img).enhance(0.7) | |
| # Step 5: Add slight blur to mimic soft pencil texture | |
| img = img.filter(ImageFilter.GaussianBlur(radius=0.5)) | |
| # Step 6: Edge enhance for sketch lines | |
| img = img.filter(ImageFilter.EDGE_ENHANCE) | |
| self.current_image = img | |
| self.display_image(img) | |
| # === ACTIONS === | |
| def undo_last(self): | |
| if len(self.history) > 1: | |
| self.history.pop() | |
| self.current_image = self.history[-1].copy() | |
| self.display_image(self.current_image) | |
| else: | |
| messagebox.showinfo("Undo", "No more undo history!") | |
| def reset_image(self): | |
| if self.original_image: | |
| self.current_image = self.original_image.copy() | |
| self.history = [self.current_image.copy()] | |
| self.display_image(self.current_image) | |
| def save_image(self): | |
| if not self.check_image(): return | |
| file_path = filedialog.asksaveasfilename( | |
| defaultextension=".png", | |
| filetypes=[("PNG", "*.png"), ("JPEG", "*.jpg"), ("WebP", "*.webp")] | |
| ) | |
| if file_path: | |
| try: | |
| self.current_image.save(file_path, quality=95) | |
| messagebox.showinfo("Saved!", f"Saved to:\n{file_path}") | |
| except Exception as e: | |
| messagebox.showerror("Error", f"Save failed: {e}") | |
| def batch_save(self): | |
| if not self.check_image(): return | |
| folder = filedialog.askdirectory(title="Select Output Folder") | |
| if folder: | |
| try: | |
| timestamp = datetime.now().strftime("%Y%m%d_%H%M%S") | |
| filename = f"dr_magic_{timestamp}.png" | |
| path = os.path.join(folder, filename) | |
| self.current_image.save(path, quality=95) | |
| messagebox.showinfo("Batch Saved!", f"Saved to:\n{path}") | |
| except Exception as e: | |
| messagebox.showerror("Error", str(e)) | |
| # === IMAGE TOOLS === | |
| def expand_canvas(self): | |
| if not self.check_image(): return | |
| self.save_to_history() | |
| # Ask for expansion amount | |
| expansion = 100 # pixels on each side | |
| img = self.current_image | |
| width, height = img.size | |
| new_width = width + (expansion * 2) | |
| new_height = height + (expansion * 2) | |
| # Create new canvas | |
| expanded = Image.new('RGB', (new_width, new_height), (20, 20, 20)) | |
| # Paste original in center | |
| expanded.paste(img, (expansion, expansion)) | |
| self.current_image = expanded | |
| self.display_image(self.current_image) | |
| messagebox.showinfo("Expanded!", f"Canvas expanded by {expansion}px on each side") | |
| def upscale_image(self): | |
| if not self.check_image(): return | |
| self.save_to_history() | |
| img = self.current_image | |
| new_size = (img.width * 2, img.height * 2) | |
| # Use LANCZOS for quality upscaling | |
| upscaled = img.resize(new_size, Image.Resampling.LANCZOS) | |
| self.current_image = upscaled | |
| self.display_image(self.current_image) | |
| messagebox.showinfo("Upscaled!", f"Image doubled to {new_size[0]}x{new_size[1]}") | |
| def style_transfer(self): | |
| if not self.check_image(): return | |
| style_path = filedialog.askopenfilename( | |
| title="Select Style Reference Image", | |
| filetypes=[("Images", "*.png *.jpg *.jpeg"), ("All", "*.*")] | |
| ) | |
| if not style_path: | |
| return | |
| self.save_to_history() | |
| try: | |
| style_img = Image.open(style_path).convert('RGB') | |
| # Simple style transfer using color statistics | |
| content = self.current_image | |
| # Get color statistics from style image | |
| style_array = np.array(style_img) | |
| content_array = np.array(content) | |
| # Match mean and std of each channel | |
| for i in range(3): # RGB channels | |
| content_mean = content_array[:,:,i].mean() | |
| content_std = content_array[:,:,i].std() | |
| style_mean = style_array[:,:,i].mean() | |
| style_std = style_array[:,:,i].std() | |
| # Transfer statistics | |
| content_array[:,:,i] = (content_array[:,:,i] - content_mean) / (content_std + 1e-5) | |
| content_array[:,:,i] = content_array[:,:,i] * style_std + style_mean | |
| # Clip to valid range | |
| content_array = np.clip(content_array, 0, 255).astype(np.uint8) | |
| self.current_image = Image.fromarray(content_array) | |
| self.display_image(self.current_image) | |
| messagebox.showinfo("Style Applied!", "Style transfer complete!") | |
| except Exception as e: | |
| messagebox.showerror("Error", f"Style transfer failed: {e}") | |
| def rotate_90(self): | |
| if not self.check_image(): return | |
| self.save_to_history() | |
| self.current_image = self.current_image.rotate(-90, expand=True) | |
| self.display_image(self.current_image) | |
| def flip_horizontal(self): | |
| if not self.check_image(): return | |
| self.save_to_history() | |
| self.current_image = self.current_image.transpose(Image.FLIP_LEFT_RIGHT) | |
| self.display_image(self.current_image) | |
| def flip_vertical(self): | |
| if not self.check_image(): return | |
| self.save_to_history() | |
| self.current_image = self.current_image.transpose(Image.FLIP_TOP_BOTTOM) | |
| self.display_image(self.current_image) | |
| if __name__ == "__main__": | |
| root = tk.Tk() | |
| app = DrImageMagicFull(root) | |
| root.mainloop() | |