|
|
import os |
|
|
from config.settings import ( |
|
|
OPENAI_API_KEY, ANTHROPIC_API_KEY, REMOVE_BG_API_KEY, DEEPSEEK_API_KEY, |
|
|
GRADIO_SERVER_NAME, GRADIO_SERVER_PORT |
|
|
) |
|
|
from config.logging_config import setup_logging |
|
|
from src.image_processing import ImageProcessor |
|
|
from src.ai_image_generator import AIImageGenerator |
|
|
from src.background_removal import BackgroundRemover |
|
|
from src.image_analysis import ImageAnalysis |
|
|
from src.custom_filters import apply_custom_filter |
|
|
from src.resize_crop import resize_crop_image |
|
|
import logging |
|
|
import io |
|
|
import zipfile |
|
|
import tempfile |
|
|
from PIL import Image |
|
|
|
|
|
setup_logging() |
|
|
logger = logging.getLogger(__name__) |
|
|
|
|
|
import gradio as gr |
|
|
|
|
|
SUPPORTED_FORMATS = [ |
|
|
"JPEG", "JPG", "PNG", "BMP", "TIFF", "TIF", "WEBP", "GIF", |
|
|
"ICO", "EPS", "PDF", "PSD", "SVG", "HEIC", "AVIF", "JXL" |
|
|
] |
|
|
ENHANCEMENT_OPTIONS = [ |
|
|
"AI Super Resolution", "Noise Reduction", "Color Enhancement", "Brightness/Contrast", "Sharpening", "HDR Effect", "Vintage Filter", "Black & White", "Sepia", "Vignette", "Blur Background" |
|
|
] |
|
|
|
|
|
processor = ImageProcessor() |
|
|
ai_generator = AIImageGenerator(openai_key=OPENAI_API_KEY, anthropic_key=ANTHROPIC_API_KEY) |
|
|
bg_remover = BackgroundRemover() |
|
|
|
|
|
|
|
|
AI_MODELS = { |
|
|
"OpenAI DALL-E 3": "dall-e-3", |
|
|
"OpenAI DALL-E 2": "dall-e-2", |
|
|
"Anthropic Claude (via API)": "claude-3-5-sonnet-20241022", |
|
|
"DeepSeek": "deepseek-chat" |
|
|
} |
|
|
BG_REMOVAL_SERVICES = { |
|
|
"Remove.bg": "removebg", |
|
|
"Removal.ai": "removelai", |
|
|
"Local rembg": "local", |
|
|
"Clipdrop": "clipdrop" |
|
|
} |
|
|
|
|
|
|
|
|
ANDROID_ICON_SIZES = { |
|
|
"mdpi": 48, |
|
|
"hdpi": 72, |
|
|
"xhdpi": 96, |
|
|
"xxhdpi": 144, |
|
|
"xxxhdpi": 192, |
|
|
} |
|
|
|
|
|
def make_android_icons_zip(image: Image.Image) -> str: |
|
|
import tempfile |
|
|
with tempfile.NamedTemporaryFile(suffix=".zip", delete=False) as tmp: |
|
|
with zipfile.ZipFile(tmp, mode="w") as zf: |
|
|
for density, size in ANDROID_ICON_SIZES.items(): |
|
|
icon = image.resize((size, size), Image.LANCZOS) |
|
|
icon_bytes = io.BytesIO() |
|
|
icon.save(icon_bytes, format="PNG") |
|
|
icon_bytes.seek(0) |
|
|
zf.writestr(f"ic_launcher_{density}.png", icon_bytes.read()) |
|
|
return tmp.name |
|
|
|
|
|
|
|
|
with gr.Blocks(title="Advanced Image Processing Suite") as demo: |
|
|
|
|
|
gr.Markdown("# Advanced Image Processing Suite") |
|
|
gr.Markdown("Professional-grade image processing, AI generation, and enhancement tools") |
|
|
|
|
|
with gr.Tabs() as tabs: |
|
|
|
|
|
with gr.Tab("Format"): |
|
|
gr.Markdown("### Convert images between multiple formats with advanced options") |
|
|
|
|
|
with gr.Row(): |
|
|
with gr.Column(): |
|
|
conv_input = gr.Image(label="Upload Image", type="filepath") |
|
|
conv_format = gr.Dropdown( |
|
|
choices=SUPPORTED_FORMATS, |
|
|
value="PNG", |
|
|
label="Target Format" |
|
|
) |
|
|
conv_quality = gr.Slider( |
|
|
minimum=10, maximum=100, value=95, |
|
|
label="Quality (for JPEG/WebP)", step=5 |
|
|
) |
|
|
conv_btn = gr.Button("Convert Image", variant="primary") |
|
|
|
|
|
with gr.Column(): |
|
|
conv_output = gr.File(label="Download Converted Image") |
|
|
conv_status = gr.Textbox(label="Status", interactive=False) |
|
|
|
|
|
conv_btn.click( |
|
|
fn=lambda img, fmt, qual: processor.convert_image(img, fmt, qual) if img else (None, "Please upload an image"), |
|
|
inputs=[conv_input, conv_format, conv_quality], |
|
|
outputs=[conv_output, conv_status] |
|
|
) |
|
|
|
|
|
|
|
|
with gr.Tab("AI Gen"): |
|
|
gr.Markdown("### Generate stunning images using state-of-the-art AI models") |
|
|
|
|
|
with gr.Row(): |
|
|
with gr.Column(): |
|
|
gr.Markdown("#### API Configuration") |
|
|
openai_key = gr.Textbox( |
|
|
label="OpenAI API Key", |
|
|
type="password", |
|
|
placeholder="sk-..." |
|
|
) |
|
|
anthropic_key = gr.Textbox( |
|
|
label="Anthropic API Key", |
|
|
type="password", |
|
|
placeholder="sk-ant-..." |
|
|
) |
|
|
deepseek_key = gr.Textbox( |
|
|
label="DeepSeek API Key", |
|
|
type="password", |
|
|
placeholder="sk-..." |
|
|
) |
|
|
|
|
|
save_keys_btn = gr.Button("Save API Keys") |
|
|
key_status = gr.Textbox(label="Key Status", interactive=False) |
|
|
|
|
|
with gr.Row(): |
|
|
with gr.Column(): |
|
|
ai_prompt = gr.Textbox( |
|
|
label="Image Prompt", |
|
|
placeholder="A serene landscape with mountains and a lake at sunset...", |
|
|
lines=3 |
|
|
) |
|
|
ai_model = gr.Dropdown( |
|
|
choices=list(AI_MODELS.keys()), |
|
|
value="OpenAI DALL-E 3", |
|
|
label="AI Model" |
|
|
) |
|
|
ai_size = gr.Dropdown( |
|
|
choices=["256x256", "512x512", "1024x1024", "1792x1024", "1024x1792"], |
|
|
value="1024x1024", |
|
|
label="Image Size" |
|
|
) |
|
|
|
|
|
generate_btn = gr.Button("Generate Image", variant="primary") |
|
|
|
|
|
with gr.Column(): |
|
|
ai_output = gr.Image(label="Generated Image") |
|
|
ai_status = gr.Textbox(label="Generation Status", interactive=False) |
|
|
|
|
|
|
|
|
def save_api_keys(openai, anthropic, deepseek): |
|
|
ai_generator.openai_key = openai |
|
|
ai_generator.anthropic_key = anthropic |
|
|
|
|
|
saved_keys = [] |
|
|
if openai: |
|
|
saved_keys.append("OpenAI") |
|
|
if anthropic: |
|
|
saved_keys.append("Anthropic") |
|
|
if deepseek: |
|
|
saved_keys.append("DeepSeek") |
|
|
if saved_keys: |
|
|
return f"Saved keys for: {', '.join(saved_keys)}" |
|
|
return "No keys provided" |
|
|
|
|
|
save_keys_btn.click( |
|
|
fn=save_api_keys, |
|
|
inputs=[openai_key, anthropic_key, deepseek_key], |
|
|
outputs=key_status |
|
|
) |
|
|
|
|
|
|
|
|
def generate_image(prompt, model, size, openai, anthropic): |
|
|
if not prompt: |
|
|
return None, "Please enter a prompt" |
|
|
if "OpenAI" in model: |
|
|
model_name = AI_MODELS[model] |
|
|
temp_gen = AIImageGenerator(openai_key=openai, anthropic_key=anthropic) |
|
|
return temp_gen.generate_image_openai(prompt, model_name, size) |
|
|
elif "Anthropic" in model: |
|
|
temp_gen = AIImageGenerator(openai_key=openai, anthropic_key=anthropic) |
|
|
return temp_gen.generate_image_anthropic(prompt) |
|
|
else: |
|
|
return None, f"{model} not yet implemented" |
|
|
|
|
|
generate_btn.click( |
|
|
fn=generate_image, |
|
|
inputs=[ai_prompt, ai_model, ai_size, openai_key, anthropic_key], |
|
|
outputs=[ai_output, ai_status] |
|
|
) |
|
|
|
|
|
|
|
|
with gr.Tab("Enhance"): |
|
|
gr.Markdown("### Enhance your images with professional-grade filters and AI") |
|
|
|
|
|
with gr.Row(): |
|
|
with gr.Column(): |
|
|
enhance_input = gr.Image(label="Upload Image to Enhance", type="filepath") |
|
|
enhance_type = gr.Dropdown( |
|
|
choices=ENHANCEMENT_OPTIONS, |
|
|
value="Color Enhancement", |
|
|
label="Enhancement Type" |
|
|
) |
|
|
enhance_intensity = gr.Slider( |
|
|
minimum=0.1, maximum=2.0, value=1.0, step=0.1, |
|
|
label="Enhancement Intensity" |
|
|
) |
|
|
enhance_btn = gr.Button("Enhance Image", variant="primary") |
|
|
|
|
|
with gr.Column(): |
|
|
enhance_output = gr.Image(label="Enhanced Image") |
|
|
enhance_status = gr.Textbox(label="Enhancement Status", interactive=False) |
|
|
|
|
|
enhance_btn.click( |
|
|
fn=lambda img, enh_type, intensity: processor.enhance_image(img, enh_type, intensity) if img else (None, "Please upload an image"), |
|
|
inputs=[enhance_input, enhance_type, enhance_intensity], |
|
|
outputs=[enhance_output, enhance_status] |
|
|
) |
|
|
|
|
|
|
|
|
with gr.Tab("BG Remove"): |
|
|
gr.Markdown("### Remove backgrounds instantly with AI-powered tools") |
|
|
|
|
|
with gr.Row(): |
|
|
with gr.Column(): |
|
|
gr.Markdown("#### API Keys for Premium Services") |
|
|
removebg_key = gr.Textbox( |
|
|
label="Remove.bg API Key", |
|
|
type="password", |
|
|
placeholder="Your Remove.bg API key" |
|
|
) |
|
|
save_bg_key_btn = gr.Button("Save Remove.bg Key") |
|
|
bg_key_status = gr.Textbox(label="API Status", interactive=False) |
|
|
|
|
|
with gr.Row(): |
|
|
with gr.Column(): |
|
|
bg_input = gr.Image(label="Upload Image", type="filepath") |
|
|
bg_service = gr.Dropdown( |
|
|
choices=list(BG_REMOVAL_SERVICES.keys()), |
|
|
value="Local rembg", |
|
|
label="Background Removal Service" |
|
|
) |
|
|
bg_btn = gr.Button("Remove Background", variant="primary") |
|
|
|
|
|
with gr.Column(): |
|
|
bg_output = gr.Image(label="Result (Transparent Background)") |
|
|
bg_status = gr.Textbox(label="Processing Status", interactive=False) |
|
|
|
|
|
|
|
|
def save_bg_api_key(key): |
|
|
bg_remover.removebg_key = key |
|
|
if key: |
|
|
return "Remove.bg API key saved" |
|
|
return "No key provided" |
|
|
save_bg_key_btn.click( |
|
|
fn=save_bg_api_key, |
|
|
inputs=removebg_key, |
|
|
outputs=bg_key_status |
|
|
) |
|
|
|
|
|
def remove_bg(img, service, removebg): |
|
|
if not img: |
|
|
return None, "Please upload an image" |
|
|
service_key = BG_REMOVAL_SERVICES.get(service, "local") |
|
|
if service_key == "local": |
|
|
return bg_remover.remove_local(img) |
|
|
elif service_key == "removebg": |
|
|
temp_remover = BackgroundRemover(removebg_key=removebg) |
|
|
return temp_remover.remove_with_removebg(img) |
|
|
else: |
|
|
return None, f"{service} not implemented" |
|
|
bg_btn.click( |
|
|
fn=remove_bg, |
|
|
inputs=[bg_input, bg_service, removebg_key], |
|
|
outputs=[bg_output, bg_status] |
|
|
) |
|
|
|
|
|
|
|
|
with gr.Tab("Batch"): |
|
|
gr.Markdown("### Process multiple images simultaneously") |
|
|
|
|
|
with gr.Row(): |
|
|
with gr.Column(): |
|
|
batch_files = gr.Files(label="Upload Multiple Images", file_types=["image"]) |
|
|
batch_operation = gr.Dropdown( |
|
|
choices=["Format Conversion", "Enhancement", "Background Removal"], |
|
|
value="Format Conversion", |
|
|
label="Batch Operation" |
|
|
) |
|
|
batch_btn = gr.Button("Process Batch", variant="primary") |
|
|
|
|
|
with gr.Column(): |
|
|
batch_output = gr.Files(label="Download Processed Images") |
|
|
batch_status = gr.Textbox(label="Batch Status", interactive=False, lines=5) |
|
|
|
|
|
|
|
|
def process_batch(files, operation): |
|
|
if not files: |
|
|
return None, "Please upload images for batch processing" |
|
|
results = [] |
|
|
status_messages = [] |
|
|
for i, file in enumerate(files): |
|
|
try: |
|
|
if operation == "Format Conversion": |
|
|
result, msg = processor.convert_image(file.name, "PNG") |
|
|
elif operation == "Enhancement": |
|
|
result, msg = processor.enhance_image(file.name, "Color Enhancement") |
|
|
elif operation == "Background Removal": |
|
|
result, msg = bg_remover.remove_local(file.name) |
|
|
if result: |
|
|
results.append(result) |
|
|
status_messages.append(f"File {i+1}: {msg}") |
|
|
except Exception as e: |
|
|
status_messages.append(f"File {i+1}: Error - {str(e)}") |
|
|
return results if results else None, "\n".join(status_messages) |
|
|
batch_btn.click( |
|
|
fn=process_batch, |
|
|
inputs=[batch_files, batch_operation], |
|
|
outputs=[batch_output, batch_status] |
|
|
) |
|
|
|
|
|
|
|
|
with gr.Tab("Tools"): |
|
|
gr.Markdown("### Professional image analysis and specialized processing") |
|
|
|
|
|
with gr.Tabs(): |
|
|
with gr.Tab("Image Analysis"): |
|
|
with gr.Row(): |
|
|
with gr.Column(): |
|
|
analysis_input = gr.Image(label="Upload Image for Analysis", type="filepath") |
|
|
analysis_btn = gr.Button("Analyze Image", variant="primary") |
|
|
|
|
|
with gr.Column(): |
|
|
analysis_output = gr.JSON(label="Image Analysis Results") |
|
|
|
|
|
|
|
|
def analyze_image(img_path): |
|
|
return ImageAnalysis.analyze_image(img_path) |
|
|
analysis_btn.click( |
|
|
fn=analyze_image, |
|
|
inputs=analysis_input, |
|
|
outputs=analysis_output |
|
|
) |
|
|
|
|
|
with gr.Tab("Custom Filters"): |
|
|
with gr.Row(): |
|
|
with gr.Column(): |
|
|
filter_input = gr.Image(label="Upload Image", type="filepath") |
|
|
|
|
|
|
|
|
brightness = gr.Slider(-100, 100, 0, label="Brightness") |
|
|
contrast = gr.Slider(-100, 100, 0, label="Contrast") |
|
|
saturation = gr.Slider(-100, 100, 0, label="Saturation") |
|
|
hue_shift = gr.Slider(-180, 180, 0, label="Hue Shift") |
|
|
|
|
|
apply_filter_btn = gr.Button("Apply Custom Filter", variant="primary") |
|
|
|
|
|
with gr.Column(): |
|
|
filter_output = gr.Image(label="Filtered Image") |
|
|
filter_status = gr.Textbox(label="Filter Status", interactive=False) |
|
|
|
|
|
|
|
|
apply_filter_btn.click( |
|
|
fn=apply_custom_filter, |
|
|
inputs=[filter_input, brightness, contrast, saturation, hue_shift], |
|
|
outputs=[filter_output, filter_status] |
|
|
) |
|
|
|
|
|
with gr.Tab("Resize & Crop"): |
|
|
with gr.Row(): |
|
|
with gr.Column(): |
|
|
resize_input = gr.Image(label="Upload Image", type="filepath") |
|
|
|
|
|
resize_mode = gr.Radio( |
|
|
choices=["Resize", "Crop", "Smart Crop", "Canvas Resize"], |
|
|
value="Resize", |
|
|
label="Operation Mode" |
|
|
) |
|
|
|
|
|
new_width = gr.Number(label="Width", value=800) |
|
|
new_height = gr.Number(label="Height", value=600) |
|
|
|
|
|
maintain_ratio = gr.Checkbox(label="Maintain Aspect Ratio", value=True) |
|
|
resize_quality = gr.Dropdown( |
|
|
choices=["NEAREST", "LANCZOS", "BILINEAR", "BICUBIC"], |
|
|
value="LANCZOS", |
|
|
label="Resize Quality" |
|
|
) |
|
|
|
|
|
resize_btn = gr.Button("Process Image", variant="primary") |
|
|
|
|
|
with gr.Column(): |
|
|
resize_output = gr.Image(label="Processed Image") |
|
|
resize_status = gr.Textbox(label="Processing Status", interactive=False) |
|
|
|
|
|
|
|
|
resize_btn.click( |
|
|
fn=resize_crop_image, |
|
|
inputs=[resize_input, resize_mode, new_width, new_height, maintain_ratio, resize_quality], |
|
|
outputs=[resize_output, resize_status] |
|
|
) |
|
|
|
|
|
|
|
|
with gr.Tab("Android Icons"): |
|
|
gr.Markdown("### Generate all Android app icon sizes and download as a ZIP") |
|
|
|
|
|
with gr.Row(): |
|
|
with gr.Column(): |
|
|
android_icon_input = gr.Image(type="pil", label="Upload Icon Source Image") |
|
|
android_icon_btn = gr.Button("Generate Android Icons", variant="primary") |
|
|
|
|
|
with gr.Column(): |
|
|
android_icon_zip = gr.File(label="Download Icons ZIP") |
|
|
android_status = gr.Textbox(label="Generation Status", interactive=False) |
|
|
|
|
|
def handle_android_icon(image): |
|
|
if image is None: |
|
|
return None, "Please upload an image" |
|
|
try: |
|
|
zip_path = make_android_icons_zip(image) |
|
|
return zip_path, "Android icons generated successfully!" |
|
|
except Exception as e: |
|
|
return None, f"Error generating icons: {str(e)}" |
|
|
|
|
|
android_icon_btn.click( |
|
|
fn=handle_android_icon, |
|
|
inputs=android_icon_input, |
|
|
outputs=[android_icon_zip, android_status] |
|
|
) |
|
|
|
|
|
|
|
|
|
|
|
if __name__ == "__main__": |
|
|
|
|
|
|
|
|
launch_kwargs = {} |
|
|
|
|
|
if os.environ.get("GRADIO_SHARE", "false").lower() == "true": |
|
|
launch_kwargs["share"] = True |
|
|
if os.environ.get("GRADIO_FAVICON_PATH"): |
|
|
launch_kwargs["favicon_path"] = os.environ["GRADIO_FAVICON_PATH"] |
|
|
if os.environ.get("GRADIO_SSL_VERIFY", "false").lower() == "true": |
|
|
launch_kwargs["ssl_verify"] = True |
|
|
if os.environ.get("GRADIO_SHOW_API", "false").lower() == "true": |
|
|
launch_kwargs["show_api"] = True |
|
|
demo.launch(**launch_kwargs) |