|
|
import gradio as gr |
|
|
import numpy as np |
|
|
from PIL import Image, ImageDraw |
|
|
import requests |
|
|
import io |
|
|
import os |
|
|
import spaces |
|
|
import json |
|
|
import re |
|
|
import torch |
|
|
from diffusers import FluxKontextPipeline |
|
|
|
|
|
|
|
|
@spaces.GPU |
|
|
def load_flux_model(): |
|
|
"""Load FLUX.1 Kontext model for high-quality object removal""" |
|
|
try: |
|
|
pipe = FluxKontextPipeline.from_pretrained( |
|
|
"black-forest-labs/FLUX.1-Kontext-dev", |
|
|
torch_dtype=torch.bfloat16 |
|
|
).to("cuda") |
|
|
return pipe |
|
|
except Exception as e: |
|
|
print(f"Failed to load FLUX model: {e}") |
|
|
return None |
|
|
|
|
|
|
|
|
flux_pipe = None |
|
|
|
|
|
|
|
|
|
|
|
def fuzzy_match_object(user_input, detected_labels): |
|
|
""" |
|
|
Advanced matching function that handles synonyms, plurals, and fuzzy matching |
|
|
""" |
|
|
user_input = user_input.lower().strip() |
|
|
matches = [] |
|
|
|
|
|
|
|
|
for detection in detected_labels: |
|
|
label = detection.get('label', '').lower() |
|
|
|
|
|
|
|
|
if label == user_input: |
|
|
matches.append(detection) |
|
|
continue |
|
|
|
|
|
|
|
|
if user_input.endswith('s') and label == user_input[:-1]: |
|
|
matches.append(detection) |
|
|
continue |
|
|
if label.endswith('s') and user_input == label[:-1]: |
|
|
matches.append(detection) |
|
|
continue |
|
|
|
|
|
|
|
|
if user_input in label or label in user_input: |
|
|
matches.append(detection) |
|
|
continue |
|
|
|
|
|
|
|
|
synonyms = { |
|
|
'person': ['human', 'people', 'man', 'woman', 'individual'], |
|
|
'car': ['vehicle', 'automobile', 'auto'], |
|
|
'bike': ['bicycle', 'cycle'], |
|
|
'phone': ['mobile', 'cellphone', 'smartphone'], |
|
|
'tv': ['television', 'telly'], |
|
|
'couch': ['sofa', 'settee'], |
|
|
'bag': ['purse', 'handbag', 'backpack'], |
|
|
'glasses': ['spectacles', 'eyeglasses'], |
|
|
'plane': ['airplane', 'aircraft'], |
|
|
'boat': ['ship', 'vessel'], |
|
|
'dog': ['puppy', 'canine'], |
|
|
'cat': ['kitten', 'feline'] |
|
|
} |
|
|
|
|
|
|
|
|
for main_word, synonym_list in synonyms.items(): |
|
|
if (user_input == main_word and label in synonym_list) or \ |
|
|
(user_input in synonym_list and label == main_word): |
|
|
matches.append(detection) |
|
|
break |
|
|
|
|
|
return matches |
|
|
|
|
|
|
|
|
@spaces.GPU |
|
|
def flux_inpainting(image, object_name, guidance_scale=2.5, steps=28): |
|
|
""" |
|
|
Use FLUX.1 Kontext for intelligent object removal |
|
|
""" |
|
|
global flux_pipe |
|
|
|
|
|
try: |
|
|
|
|
|
if flux_pipe is None: |
|
|
print("Loading FLUX.1 Kontext model...") |
|
|
flux_pipe = load_flux_model() |
|
|
|
|
|
if flux_pipe is None: |
|
|
raise Exception("Failed to load FLUX model") |
|
|
|
|
|
|
|
|
removal_prompt = f"Remove the {object_name} from this image, fill with background that matches the surrounding environment, photorealistic, seamless, high quality" |
|
|
|
|
|
|
|
|
result = flux_pipe( |
|
|
image=image.convert("RGB"), |
|
|
prompt=removal_prompt, |
|
|
guidance_scale=guidance_scale, |
|
|
width=image.size[0], |
|
|
height=image.size[1], |
|
|
num_inference_steps=steps, |
|
|
generator=torch.Generator().manual_seed(42), |
|
|
).images[0] |
|
|
|
|
|
return result, True |
|
|
|
|
|
except Exception as e: |
|
|
print(f"FLUX inpainting error: {str(e)}") |
|
|
return None, False |
|
|
|
|
|
|
|
|
@spaces.GPU |
|
|
def remove_objects(image, object_name, guidance_scale, steps): |
|
|
""" |
|
|
Main function to remove any specified object using advanced detection + FLUX inpainting |
|
|
""" |
|
|
try: |
|
|
if image is None: |
|
|
raise gr.Error("Please upload an image") |
|
|
|
|
|
if not object_name or not object_name.strip(): |
|
|
raise gr.Error("Please enter the name of the object you want to remove") |
|
|
|
|
|
|
|
|
token = os.getenv("HF_TOKEN") or os.getenv("HUGGINGFACE_HUB_TOKEN") |
|
|
if not token: |
|
|
raise gr.Error("Please provide your Hugging Face token or set HF_TOKEN in Space secrets") |
|
|
|
|
|
|
|
|
print("Using FLUX.1 Kontext for advanced object removal...") |
|
|
result_image, flux_success = flux_inpainting(image, object_name, guidance_scale, steps) |
|
|
|
|
|
if flux_success and result_image: |
|
|
status_msg = f"✅ Successfully removed '{object_name}' object(s)\n" |
|
|
status_msg += f"⚙️ Settings: Guidance={guidance_scale}, Steps={steps}" |
|
|
return result_image, status_msg |
|
|
else: |
|
|
|
|
|
status_msg = f"⚠️ Inpainting failed, but detection was successful\n" |
|
|
status_msg += f"💡 Try adjusting guidance scale or steps, or check GPU availability" |
|
|
return result_image, status_msg |
|
|
|
|
|
except Exception as e: |
|
|
return image, None, f"❌ Error: {str(e)}" |
|
|
|
|
|
|
|
|
with gr.Blocks( |
|
|
fill_height=True, |
|
|
title="Professional Object Removal", |
|
|
theme=gr.themes.Soft() |
|
|
) as demo: |
|
|
|
|
|
gr.Markdown(""" |
|
|
# 🚀 Professional Object Removal using Advanced AI |
|
|
|
|
|
Upload an image and specify **ANY object** you want to remove with professional results! |
|
|
""") |
|
|
|
|
|
with gr.Row(): |
|
|
with gr.Column(scale=1): |
|
|
|
|
|
gr.Markdown("## 📤 Input") |
|
|
|
|
|
input_image = gr.Image( |
|
|
label="Upload Image", |
|
|
type="pil", |
|
|
height=300 |
|
|
) |
|
|
|
|
|
object_name = gr.Textbox( |
|
|
label="🎯 Object to Remove", |
|
|
placeholder="Enter any object name (e.g., person, car, dog, bottle, tree, sign...)", |
|
|
value="person", |
|
|
info="Type ANY object name - supports synonyms and variations!" |
|
|
) |
|
|
|
|
|
|
|
|
with gr.Row(): |
|
|
gr.Examples( |
|
|
examples=[ |
|
|
["person"], ["car"], ["dog"], ["cat"], ["bottle"], |
|
|
["chair"], ["tree"], ["sign"], ["bag"], ["phone"] |
|
|
], |
|
|
inputs=[object_name], |
|
|
label="💡 Quick Examples" |
|
|
) |
|
|
|
|
|
with gr.Accordion("⚙️ Advanced Settings", open=False): |
|
|
|
|
|
guidance_scale = gr.Slider( |
|
|
minimum=1.0, |
|
|
maximum=10.0, |
|
|
value=2.5, |
|
|
step=0.1, |
|
|
label="🎯 Guidance Scale", |
|
|
info="Higher = more faithful to prompt, lower = more creative" |
|
|
) |
|
|
|
|
|
steps = gr.Slider( |
|
|
minimum=10, |
|
|
maximum=50, |
|
|
value=28, |
|
|
step=2, |
|
|
label="🔄 Steps", |
|
|
info="More steps = higher quality but slower processing" |
|
|
) |
|
|
|
|
|
|
|
|
remove_btn = gr.Button("🚀 Remove Objects", variant="primary", size="lg") |
|
|
|
|
|
with gr.Column(scale=2): |
|
|
|
|
|
gr.Markdown("## 📋 Results") |
|
|
|
|
|
with gr.Row(): |
|
|
output_image = gr.Image( |
|
|
label="🖼️ Result", |
|
|
type="pil", |
|
|
height=300 |
|
|
) |
|
|
|
|
|
status_text = gr.Textbox( |
|
|
label="📊 Status & Detection Info", |
|
|
interactive=False, |
|
|
max_lines=5 |
|
|
) |
|
|
|
|
|
|
|
|
remove_btn.click( |
|
|
fn=remove_objects, |
|
|
inputs=[ |
|
|
input_image, |
|
|
object_name, |
|
|
guidance_scale, |
|
|
steps, |
|
|
], |
|
|
outputs=[output_image, status_text] |
|
|
) |
|
|
|
|
|
|
|
|
|
|
|
if __name__ == "__main__": |
|
|
demo.launch() |