Revrse commited on
Commit
d709b64
·
verified ·
1 Parent(s): cdc978b

Update app.py

Browse files
Files changed (1) hide show
  1. app.py +596 -241
app.py CHANGED
@@ -1,255 +1,610 @@
1
- import gradio as gr
2
- import numpy as np
3
- from PIL import Image, ImageDraw
4
- import requests
5
- import io
6
- import os
7
- import spaces
8
- import json
9
- import re
10
- import torch
11
- from diffusers import FluxKontextPipeline
12
-
13
- # Initialize FLUX model for advanced inpainting
14
- @spaces.GPU
15
- def load_flux_model():
16
- """Load FLUX.1 Kontext model for high-quality object removal"""
17
- try:
18
- pipe = FluxKontextPipeline.from_pretrained(
19
- "black-forest-labs/FLUX.1-Kontext-dev",
20
- torch_dtype=torch.bfloat16
21
- ).to("cuda")
22
- return pipe
23
- except Exception as e:
24
- print(f"Failed to load FLUX model: {e}")
25
- return None
26
-
27
- # Global variable to store the model (loaded once)
28
- flux_pipe = None
29
-
30
- # Initialize object detection using proven working models
31
-
32
- def fuzzy_match_object(user_input, detected_labels):
33
- """
34
- Advanced matching function that handles synonyms, plurals, and fuzzy matching
35
- """
36
- user_input = user_input.lower().strip()
37
- matches = []
38
-
39
- # Direct matching
40
- for detection in detected_labels:
41
- label = detection.get('label', '').lower()
42
-
43
- # Exact match
44
- if label == user_input:
45
- matches.append(detection)
46
- continue
47
-
48
- # Handle plurals
49
- if user_input.endswith('s') and label == user_input[:-1]:
50
- matches.append(detection)
51
- continue
52
- if label.endswith('s') and user_input == label[:-1]:
53
- matches.append(detection)
54
- continue
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
55
 
56
- # Substring matching
57
- if user_input in label or label in user_input:
58
- matches.append(detection)
59
- continue
60
 
61
- # Handle common synonyms
62
- synonyms = {
63
- 'person': ['human', 'people', 'man', 'woman', 'individual'],
64
- 'car': ['vehicle', 'automobile', 'auto'],
65
- 'bike': ['bicycle', 'cycle'],
66
- 'phone': ['mobile', 'cellphone', 'smartphone'],
67
- 'tv': ['television', 'telly'],
68
- 'couch': ['sofa', 'settee'],
69
- 'bag': ['purse', 'handbag', 'backpack'],
70
- 'glasses': ['spectacles', 'eyeglasses'],
71
- 'plane': ['airplane', 'aircraft'],
72
- 'boat': ['ship', 'vessel'],
73
- 'dog': ['puppy', 'canine'],
74
- 'cat': ['kitten', 'feline']
75
  }
76
-
77
- # Check if user input matches any synonym
78
- for main_word, synonym_list in synonyms.items():
79
- if (user_input == main_word and label in synonym_list) or \
80
- (user_input in synonym_list and label == main_word):
81
- matches.append(detection)
82
- break
83
-
84
- return matches
85
-
86
-
87
- @spaces.GPU
88
- def flux_inpainting(image, object_name, guidance_scale=2.5, steps=28):
89
- """
90
- Use FLUX.1 Kontext for intelligent object removal
91
- """
92
- global flux_pipe
93
-
94
- try:
95
- # Load FLUX model if not already loaded
96
- if flux_pipe is None:
97
- print("Loading FLUX.1 Kontext model...")
98
- flux_pipe = load_flux_model()
99
 
100
- if flux_pipe is None:
101
- raise Exception("Failed to load FLUX model")
102
-
103
- # Create intelligent removal prompt
104
- removal_prompt = f"Remove the {object_name} from this image, fill with background that matches the surrounding environment, photorealistic, seamless, high quality"
105
-
106
- # Use FLUX for contextual editing
107
- result = flux_pipe(
108
- image=image.convert("RGB"),
109
- prompt=removal_prompt,
110
- guidance_scale=guidance_scale,
111
- width=image.size[0],
112
- height=image.size[1],
113
- num_inference_steps=steps,
114
- generator=torch.Generator().manual_seed(42),
115
- ).images[0]
116
-
117
- return result, True
118
-
119
- except Exception as e:
120
- print(f"FLUX inpainting error: {str(e)}")
121
- return None, False
122
-
123
-
124
- @spaces.GPU
125
- def remove_objects(image, object_name, guidance_scale, steps):
126
- """
127
- Main function to remove any specified object using advanced detection + FLUX inpainting
128
- """
129
- try:
130
- if image is None:
131
- raise gr.Error("Please upload an image")
132
-
133
- if not object_name or not object_name.strip():
134
- raise gr.Error("Please enter the name of the object you want to remove")
135
-
136
- # Try to get token from multiple sources
137
- token = os.getenv("HF_TOKEN") or os.getenv("HUGGINGFACE_HUB_TOKEN")
138
- if not token:
139
- raise gr.Error("Please provide your Hugging Face token or set HF_TOKEN in Space secrets")
140
-
141
- # Step 3: Use FLUX.1 Kontext for intelligent object removal
142
- print("Using FLUX.1 Kontext for advanced object removal...")
143
- result_image, flux_success = flux_inpainting(image, object_name, guidance_scale, steps)
144
-
145
- if flux_success and result_image:
146
- status_msg = f"✅ Successfully removed '{object_name}' object(s)\n"
147
- status_msg += f"⚙️ Settings: Guidance={guidance_scale}, Steps={steps}"
148
- return result_image, status_msg
149
- else:
150
- # Fallback: show detection areas
151
- status_msg = f"⚠️ Inpainting failed, but detection was successful\n"
152
- status_msg += f"💡 Try adjusting guidance scale or steps, or check GPU availability"
153
- return result_image, status_msg
154
 
155
- except Exception as e:
156
- return image, None, f"❌ Error: {str(e)}"
157
-
158
- # Create Gradio interface
159
- with gr.Blocks(
160
- fill_height=True,
161
- title="Professional Object Removal",
162
- theme=gr.themes.Soft()
163
- ) as demo:
164
-
165
- gr.Markdown("""
166
- # 🚀 Professional Object Removal using Advanced AI
167
-
168
- Upload an image and specify **ANY object** you want to remove with professional results!
169
- """)
170
-
171
- with gr.Row():
172
- with gr.Column(scale=1):
173
- # Input section
174
- gr.Markdown("## 📤 Input")
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
175
 
176
- input_image = gr.Image(
177
- label="Upload Image",
178
- type="pil",
179
- height=300
180
- )
181
 
182
- object_name = gr.Textbox(
183
- label="🎯 Object to Remove",
184
- placeholder="Enter any object name (e.g., person, car, dog, bottle, tree, sign...)",
185
- value="person",
186
- info="Type ANY object name - supports synonyms and variations!"
187
- )
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
188
 
189
- # Add suggestions
190
- with gr.Row():
191
- gr.Examples(
192
- examples=[
193
- ["person"], ["car"], ["dog"], ["cat"], ["bottle"],
194
- ["chair"], ["tree"], ["sign"], ["bag"], ["phone"]
195
- ],
196
- inputs=[object_name],
197
- label="💡 Quick Examples"
198
- )
199
 
200
- with gr.Accordion("⚙️ Advanced Settings", open=False):
201
-
202
- guidance_scale = gr.Slider(
203
- minimum=1.0,
204
- maximum=10.0,
205
- value=2.5,
206
- step=0.1,
207
- label="🎯 Guidance Scale",
208
- info="Higher = more faithful to prompt, lower = more creative"
209
- )
210
-
211
- steps = gr.Slider(
212
- minimum=10,
213
- maximum=50,
214
- value=28,
215
- step=2,
216
- label="🔄 Steps",
217
- info="More steps = higher quality but slower processing"
218
- )
219
 
 
 
 
 
 
 
 
 
 
220
 
221
- remove_btn = gr.Button("🚀 Remove Objects", variant="primary", size="lg")
222
-
223
- with gr.Column(scale=2):
224
- # Output section
225
- gr.Markdown("## 📋 Results")
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
226
 
227
- with gr.Row():
228
- output_image = gr.Image(
229
- label="🖼️ Result",
230
- type="pil",
231
- height=300
232
- )
233
 
234
- status_text = gr.Textbox(
235
- label="📊 Status & Detection Info",
236
- interactive=False,
237
- max_lines=5
238
- )
239
-
240
- # Event handlers
241
- remove_btn.click(
242
- fn=remove_objects,
243
- inputs=[
244
- input_image,
245
- object_name,
246
- guidance_scale,
247
- steps,
248
- ],
249
- outputs=[output_image, status_text]
250
- )
251
-
252
-
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
253
 
254
- if __name__ == "__main__":
255
- demo.launch()
 
 
 
 
 
 
1
+ <!DOCTYPE html>
2
+ <html lang="en">
3
+ <head>
4
+ <meta charset="UTF-8">
5
+ <meta name="viewport" content="width=device-width, initial-scale=1.0">
6
+ <title>AI Object Removal - Chat Interface</title>
7
+ <style>
8
+ * {
9
+ margin: 0;
10
+ padding: 0;
11
+ box-sizing: border-box;
12
+ }
13
+
14
+ body {
15
+ font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, Oxygen, Ubuntu, Cantarell, sans-serif;
16
+ background: linear-gradient(135deg, #0a0a0a 0%, #1a1a2e 50%, #16213e 100%);
17
+ color: #ffffff;
18
+ height: 100vh;
19
+ overflow: hidden;
20
+ }
21
+
22
+ .container {
23
+ display: flex;
24
+ height: 100vh;
25
+ }
26
+
27
+ /* Left Panel - Chat Interface */
28
+ .chat-panel {
29
+ width: 40%;
30
+ background: rgba(20, 20, 30, 0.8);
31
+ backdrop-filter: blur(20px);
32
+ border-right: 1px solid rgba(255, 255, 255, 0.1);
33
+ display: flex;
34
+ flex-direction: column;
35
+ }
36
+
37
+ .chat-header {
38
+ padding: 20px;
39
+ border-bottom: 1px solid rgba(255, 255, 255, 0.1);
40
+ background: rgba(0, 0, 0, 0.3);
41
+ }
42
+
43
+ .chat-header h1 {
44
+ font-size: 24px;
45
+ background: linear-gradient(45deg, #00ff88, #00ccff);
46
+ -webkit-background-clip: text;
47
+ -webkit-text-fill-color: transparent;
48
+ margin-bottom: 8px;
49
+ }
50
+
51
+ .chat-header p {
52
+ color: rgba(255, 255, 255, 0.7);
53
+ font-size: 14px;
54
+ }
55
+
56
+ .chat-messages {
57
+ flex: 1;
58
+ overflow-y: auto;
59
+ padding: 20px;
60
+ display: flex;
61
+ flex-direction: column;
62
+ gap: 16px;
63
+ }
64
+
65
+ .message {
66
+ max-width: 85%;
67
+ padding: 12px 16px;
68
+ border-radius: 18px;
69
+ font-size: 14px;
70
+ line-height: 1.4;
71
+ animation: slideIn 0.3s ease-out;
72
+ }
73
+
74
+ .message.user {
75
+ align-self: flex-end;
76
+ background: linear-gradient(135deg, #00ff88, #00ccff);
77
+ color: #000;
78
+ font-weight: 500;
79
+ }
80
+
81
+ .message.ai {
82
+ align-self: flex-start;
83
+ background: rgba(255, 255, 255, 0.1);
84
+ border: 1px solid rgba(255, 255, 255, 0.2);
85
+ }
86
+
87
+ .message.status {
88
+ align-self: center;
89
+ background: rgba(255, 193, 7, 0.2);
90
+ border: 1px solid rgba(255, 193, 7, 0.4);
91
+ color: #ffc107;
92
+ font-size: 12px;
93
+ text-align: center;
94
+ }
95
+
96
+ .image-preview {
97
+ max-width: 200px;
98
+ border-radius: 12px;
99
+ margin: 8px 0;
100
+ border: 2px solid rgba(255, 255, 255, 0.2);
101
+ }
102
+
103
+ .chat-input {
104
+ padding: 20px;
105
+ border-top: 1px solid rgba(255, 255, 255, 0.1);
106
+ background: rgba(0, 0, 0, 0.3);
107
+ }
108
+
109
+ .input-container {
110
+ display: flex;
111
+ gap: 12px;
112
+ margin-bottom: 12px;
113
+ }
114
+
115
+ .text-input {
116
+ flex: 1;
117
+ padding: 12px 16px;
118
+ background: rgba(255, 255, 255, 0.1);
119
+ border: 1px solid rgba(255, 255, 255, 0.3);
120
+ border-radius: 25px;
121
+ color: #fff;
122
+ font-size: 14px;
123
+ transition: all 0.3s ease;
124
+ }
125
+
126
+ .text-input:focus {
127
+ outline: none;
128
+ border-color: #00ff88;
129
+ box-shadow: 0 0 0 2px rgba(0, 255, 136, 0.2);
130
+ }
131
+
132
+ .text-input::placeholder {
133
+ color: rgba(255, 255, 255, 0.5);
134
+ }
135
+
136
+ .send-btn {
137
+ padding: 12px 20px;
138
+ background: linear-gradient(135deg, #00ff88, #00ccff);
139
+ border: none;
140
+ border-radius: 25px;
141
+ color: #000;
142
+ font-weight: 600;
143
+ cursor: pointer;
144
+ transition: all 0.3s ease;
145
+ font-size: 14px;
146
+ }
147
+
148
+ .send-btn:hover {
149
+ transform: translateY(-2px);
150
+ box-shadow: 0 8px 25px rgba(0, 255, 136, 0.3);
151
+ }
152
+
153
+ .send-btn:disabled {
154
+ opacity: 0.5;
155
+ cursor: not-allowed;
156
+ transform: none;
157
+ }
158
+
159
+ .file-input-container {
160
+ display: flex;
161
+ gap: 8px;
162
+ align-items: center;
163
+ }
164
+
165
+ .file-input {
166
+ display: none;
167
+ }
168
+
169
+ .file-label {
170
+ padding: 8px 16px;
171
+ background: rgba(255, 255, 255, 0.1);
172
+ border: 1px solid rgba(255, 255, 255, 0.3);
173
+ border-radius: 20px;
174
+ cursor: pointer;
175
+ font-size: 12px;
176
+ transition: all 0.3s ease;
177
+ display: flex;
178
+ align-items: center;
179
+ gap: 6px;
180
+ }
181
+
182
+ .file-label:hover {
183
+ background: rgba(255, 255, 255, 0.2);
184
+ }
185
+
186
+ .settings-panel {
187
+ padding: 12px 0;
188
+ border-top: 1px solid rgba(255, 255, 255, 0.1);
189
+ }
190
+
191
+ .setting-item {
192
+ display: flex;
193
+ justify-content: space-between;
194
+ align-items: center;
195
+ margin-bottom: 8px;
196
+ }
197
+
198
+ .setting-label {
199
+ font-size: 12px;
200
+ color: rgba(255, 255, 255, 0.7);
201
+ }
202
+
203
+ .slider {
204
+ width: 100px;
205
+ height: 4px;
206
+ background: rgba(255, 255, 255, 0.2);
207
+ border-radius: 2px;
208
+ outline: none;
209
+ -webkit-appearance: none;
210
+ }
211
+
212
+ .slider::-webkit-slider-thumb {
213
+ -webkit-appearance: none;
214
+ width: 16px;
215
+ height: 16px;
216
+ background: #00ff88;
217
+ border-radius: 50%;
218
+ cursor: pointer;
219
+ }
220
+
221
+ /* Right Panel - Output */
222
+ .output-panel {
223
+ width: 60%;
224
+ background: rgba(10, 10, 20, 0.8);
225
+ backdrop-filter: blur(20px);
226
+ display: flex;
227
+ flex-direction: column;
228
+ align-items: center;
229
+ justify-content: center;
230
+ position: relative;
231
+ }
232
+
233
+ .output-content {
234
+ text-align: center;
235
+ max-width: 90%;
236
+ }
237
+
238
+ .output-image {
239
+ max-width: 100%;
240
+ max-height: 70vh;
241
+ border-radius: 16px;
242
+ box-shadow: 0 20px 60px rgba(0, 0, 0, 0.4);
243
+ border: 2px solid rgba(255, 255, 255, 0.1);
244
+ transition: all 0.3s ease;
245
+ }
246
+
247
+ .output-image:hover {
248
+ transform: scale(1.02);
249
+ box-shadow: 0 25px 80px rgba(0, 255, 136, 0.2);
250
+ }
251
+
252
+ .placeholder {
253
+ color: rgba(255, 255, 255, 0.4);
254
+ font-size: 18px;
255
+ margin-bottom: 20px;
256
+ }
257
+
258
+ .loading {
259
+ display: none;
260
+ flex-direction: column;
261
+ align-items: center;
262
+ gap: 20px;
263
+ }
264
+
265
+ .spinner {
266
+ width: 40px;
267
+ height: 40px;
268
+ border: 3px solid rgba(255, 255, 255, 0.3);
269
+ border-top: 3px solid #00ff88;
270
+ border-radius: 50%;
271
+ animation: spin 1s linear infinite;
272
+ }
273
+
274
+ .download-btn {
275
+ margin-top: 20px;
276
+ padding: 12px 24px;
277
+ background: rgba(255, 255, 255, 0.1);
278
+ border: 1px solid rgba(255, 255, 255, 0.3);
279
+ border-radius: 25px;
280
+ color: #fff;
281
+ cursor: pointer;
282
+ transition: all 0.3s ease;
283
+ text-decoration: none;
284
+ display: inline-block;
285
+ }
286
+
287
+ .download-btn:hover {
288
+ background: rgba(255, 255, 255, 0.2);
289
+ transform: translateY(-2px);
290
+ }
291
+
292
+ @keyframes slideIn {
293
+ from {
294
+ opacity: 0;
295
+ transform: translateY(20px);
296
+ }
297
+ to {
298
+ opacity: 1;
299
+ transform: translateY(0);
300
+ }
301
+ }
302
+
303
+ @keyframes spin {
304
+ 0% { transform: rotate(0deg); }
305
+ 100% { transform: rotate(360deg); }
306
+ }
307
+
308
+ /* Scrollbar Styling */
309
+ .chat-messages::-webkit-scrollbar {
310
+ width: 6px;
311
+ }
312
+
313
+ .chat-messages::-webkit-scrollbar-track {
314
+ background: rgba(255, 255, 255, 0.1);
315
+ border-radius: 3px;
316
+ }
317
+
318
+ .chat-messages::-webkit-scrollbar-thumb {
319
+ background: rgba(255, 255, 255, 0.3);
320
+ border-radius: 3px;
321
+ }
322
+
323
+ .chat-messages::-webkit-scrollbar-thumb:hover {
324
+ background: rgba(255, 255, 255, 0.5);
325
+ }
326
+
327
+ /* Responsive Design */
328
+ @media (max-width: 768px) {
329
+ .container {
330
+ flex-direction: column;
331
+ }
332
 
333
+ .chat-panel {
334
+ width: 100%;
335
+ height: 60%;
336
+ }
337
 
338
+ .output-panel {
339
+ width: 100%;
340
+ height: 40%;
341
+ }
 
 
 
 
 
 
 
 
 
 
342
  }
343
+ </style>
344
+ </head>
345
+ <body>
346
+ <div class="container">
347
+ <!-- Left Panel - Chat Interface -->
348
+ <div class="chat-panel">
349
+ <div class="chat-header">
350
+ <h1>🚀 AI Object Removal</h1>
351
+ <p>Upload an image and tell me what to remove</p>
352
+ </div>
 
 
 
 
 
 
 
 
 
 
 
 
 
353
 
354
+ <div class="chat-messages" id="chatMessages">
355
+ <div class="message ai">
356
+ <div>👋 Hi! I'm your AI assistant for object removal. Upload an image and tell me what object you'd like me to remove from it.</div>
357
+ </div>
358
+ <div class="message ai">
359
+ <div>💡 Try objects like: person, car, dog, bottle, chair, sign, tree, phone, etc.</div>
360
+ </div>
361
+ </div>
362
+
363
+ <div class="chat-input">
364
+ <div class="input-container">
365
+ <input
366
+ type="text"
367
+ class="text-input"
368
+ id="objectInput"
369
+ placeholder="What object should I remove? (e.g., person, car, dog...)"
370
+ value="person"
371
+ />
372
+ <button class="send-btn" id="processBtn">Remove Object</button>
373
+ </div>
374
+
375
+ <div class="file-input-container">
376
+ <input type="file" class="file-input" id="imageInput" accept="image/*" />
377
+ <label for="imageInput" class="file-label">
378
+ 📷 Upload Image
379
+ </label>
380
+ <span id="fileName" style="font-size: 12px; color: rgba(255,255,255,0.6);"></span>
381
+ </div>
382
+
383
+ <div class="settings-panel">
384
+ <div class="setting-item">
385
+ <span class="setting-label">Guidance Scale: <span id="guidanceValue">2.5</span></span>
386
+ <input type="range" class="slider" id="guidanceSlider" min="1" max="10" step="0.1" value="2.5" />
387
+ </div>
388
+ <div class="setting-item">
389
+ <span class="setting-label">Steps: <span id="stepsValue">28</span></span>
390
+ <input type="range" class="slider" id="stepsSlider" min="10" max="50" step="2" value="28" />
391
+ </div>
392
+ </div>
393
+ </div>
394
+ </div>
 
 
 
 
 
 
 
 
 
 
 
 
 
395
 
396
+ <!-- Right Panel - Output -->
397
+ <div class="output-panel">
398
+ <div class="output-content" id="outputContent">
399
+ <div class="placeholder" id="placeholder">
400
+ <div style="font-size: 48px; margin-bottom: 20px;">🖼️</div>
401
+ <div>Your processed image will appear here</div>
402
+ <div style="margin-top: 10px; font-size: 14px; opacity: 0.6;">Upload an image and specify an object to remove</div>
403
+ </div>
404
+
405
+ <div class="loading" id="loading">
406
+ <div class="spinner"></div>
407
+ <div>Processing your image...</div>
408
+ <div style="font-size: 12px; opacity: 0.7;">This may take a few moments</div>
409
+ </div>
410
+ </div>
411
+ </div>
412
+ </div>
413
+
414
+ <script>
415
+ let uploadedImage = null;
416
+ let currentImageUrl = null;
417
+
418
+ // DOM elements
419
+ const chatMessages = document.getElementById('chatMessages');
420
+ const objectInput = document.getElementById('objectInput');
421
+ const processBtn = document.getElementById('processBtn');
422
+ const imageInput = document.getElementById('imageInput');
423
+ const fileName = document.getElementById('fileName');
424
+ const outputContent = document.getElementById('outputContent');
425
+ const placeholder = document.getElementById('placeholder');
426
+ const loading = document.getElementById('loading');
427
+ const guidanceSlider = document.getElementById('guidanceSlider');
428
+ const stepsSlider = document.getElementById('stepsSlider');
429
+ const guidanceValue = document.getElementById('guidanceValue');
430
+ const stepsValue = document.getElementById('stepsValue');
431
+
432
+ // Update slider values
433
+ guidanceSlider.addEventListener('input', (e) => {
434
+ guidanceValue.textContent = e.target.value;
435
+ });
436
+
437
+ stepsSlider.addEventListener('input', (e) => {
438
+ stepsValue.textContent = e.target.value;
439
+ });
440
+
441
+ // Handle image upload
442
+ imageInput.addEventListener('change', (e) => {
443
+ const file = e.target.files[0];
444
+ if (file) {
445
+ fileName.textContent = file.name;
446
+
447
+ const reader = new FileReader();
448
+ reader.onload = (e) => {
449
+ uploadedImage = e.target.result;
450
+ addMessage('user', `📷 Uploaded: ${file.name}`, uploadedImage);
451
+ addMessage('ai', '✅ Image uploaded successfully! Now tell me what object you\'d like me to remove.');
452
+ };
453
+ reader.readAsDataURL(file);
454
+ }
455
+ });
456
+
457
+ // Handle enter key
458
+ objectInput.addEventListener('keypress', (e) => {
459
+ if (e.key === 'Enter') {
460
+ processImage();
461
+ }
462
+ });
463
+
464
+ // Handle process button
465
+ processBtn.addEventListener('click', processImage);
466
+
467
+ function addMessage(type, text, imageUrl = null) {
468
+ const messageDiv = document.createElement('div');
469
+ messageDiv.className = `message ${type}`;
470
 
471
+ let content = `<div>${text}</div>`;
472
+ if (imageUrl) {
473
+ content += `<img src="${imageUrl}" class="image-preview" alt="Uploaded image" />`;
474
+ }
 
475
 
476
+ messageDiv.innerHTML = content;
477
+ chatMessages.appendChild(messageDiv);
478
+ chatMessages.scrollTop = chatMessages.scrollHeight;
479
+ }
480
+
481
+ function addStatusMessage(text) {
482
+ const messageDiv = document.createElement('div');
483
+ messageDiv.className = 'message status';
484
+ messageDiv.innerHTML = `<div>${text}</div>`;
485
+ chatMessages.appendChild(messageDiv);
486
+ chatMessages.scrollTop = chatMessages.scrollHeight;
487
+ }
488
+
489
+ function showLoading() {
490
+ placeholder.style.display = 'none';
491
+ loading.style.display = 'flex';
492
+ if (currentImageUrl) {
493
+ const existingImage = outputContent.querySelector('.output-image');
494
+ if (existingImage) {
495
+ existingImage.style.opacity = '0.3';
496
+ }
497
+ }
498
+ }
499
+
500
+ function hideLoading() {
501
+ loading.style.display = 'none';
502
+ if (currentImageUrl) {
503
+ const existingImage = outputContent.querySelector('.output-image');
504
+ if (existingImage) {
505
+ existingImage.style.opacity = '1';
506
+ }
507
+ }
508
+ }
509
+
510
+ function displayResult(imageUrl, downloadUrl = null) {
511
+ hideLoading();
512
+ placeholder.style.display = 'none';
513
 
514
+ // Remove existing image
515
+ const existingImage = outputContent.querySelector('.output-image');
516
+ const existingDownload = outputContent.querySelector('.download-btn');
517
+ if (existingImage) existingImage.remove();
518
+ if (existingDownload) existingDownload.remove();
 
 
 
 
 
519
 
520
+ // Add new image
521
+ const img = document.createElement('img');
522
+ img.src = imageUrl;
523
+ img.className = 'output-image';
524
+ img.alt = 'Processed result';
525
+ outputContent.appendChild(img);
 
 
 
 
 
 
 
 
 
 
 
 
 
526
 
527
+ // Add download button if URL provided
528
+ if (downloadUrl) {
529
+ const downloadBtn = document.createElement('a');
530
+ downloadBtn.href = downloadUrl;
531
+ downloadBtn.download = 'removed_object_result.png';
532
+ downloadBtn.className = 'download-btn';
533
+ downloadBtn.textContent = '💾 Download Result';
534
+ outputContent.appendChild(downloadBtn);
535
+ }
536
 
537
+ currentImageUrl = imageUrl;
538
+ }
539
+
540
+ function processImage() {
541
+ if (!uploadedImage) {
542
+ addMessage('ai', '❌ Please upload an image first!');
543
+ return;
544
+ }
545
+
546
+ const objectName = objectInput.value.trim();
547
+ if (!objectName) {
548
+ addMessage('ai', '❌ Please specify what object you want me to remove!');
549
+ return;
550
+ }
551
+
552
+ const guidance = guidanceSlider.value;
553
+ const steps = stepsSlider.value;
554
+
555
+ // Add user message
556
+ addMessage('user', `Remove "${objectName}" from the image (Guidance: ${guidance}, Steps: ${steps})`);
557
 
558
+ // Show processing status
559
+ addStatusMessage('🔄 Processing your request...');
560
+ showLoading();
 
 
 
561
 
562
+ // Simulate processing (replace with actual API call)
563
+ simulateProcessing(objectName, guidance, steps);
564
+ }
565
+
566
+ function simulateProcessing(objectName, guidance, steps) {
567
+ // This is a simulation - replace with your actual API endpoint
568
+ setTimeout(() => {
569
+ // Generate a mock result (you'll replace this with actual processing)
570
+ const canvas = document.createElement('canvas');
571
+ const ctx = canvas.getContext('2d');
572
+
573
+ const img = new Image();
574
+ img.onload = () => {
575
+ canvas.width = img.width;
576
+ canvas.height = img.height;
577
+
578
+ // Draw original image
579
+ ctx.drawImage(img, 0, 0);
580
+
581
+ // Add some visual indication of processing (optional)
582
+ ctx.fillStyle = 'rgba(0, 255, 136, 0.1)';
583
+ ctx.fillRect(0, 0, canvas.width, canvas.height);
584
+
585
+ // Add text overlay to show it's processed
586
+ ctx.fillStyle = 'rgba(0, 255, 136, 0.8)';
587
+ ctx.font = '20px Arial';
588
+ ctx.fillText(`${objectName} removed`, 20, 40);
589
+
590
+ const resultUrl = canvas.toDataURL();
591
+
592
+ // Display result
593
+ displayResult(resultUrl, resultUrl);
594
+
595
+ // Add success message
596
+ addMessage('ai', `✅ Successfully removed "${objectName}" from your image!`);
597
+ addStatusMessage(`⚙️ Settings used: Guidance=${guidance}, Steps=${steps}`);
598
+ };
599
+ img.src = uploadedImage;
600
+
601
+ }, 2000); // Simulate 2 second processing time
602
+ }
603
 
604
+ // Initialize with some example messages
605
+ setTimeout(() => {
606
+ addMessage('ai', '🎯 Quick tip: I work best with clear, well-lit images where the object is clearly visible.');
607
+ }, 1000);
608
+ </script>
609
+ </body>
610
+ </html>