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

Update app.py

Browse files
Files changed (1) hide show
  1. app.py +413 -597
app.py CHANGED
@@ -1,610 +1,426 @@
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>
 
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
+ def fuzzy_match_object(user_input, detected_labels):
31
+ """
32
+ Advanced matching function that handles synonyms, plurals, and fuzzy matching
33
+ """
34
+ user_input = user_input.lower().strip()
35
+ matches = []
36
+
37
+ # Direct matching
38
+ for detection in detected_labels:
39
+ label = detection.get('label', '').lower()
40
+
41
+ # Exact match
42
+ if label == user_input:
43
+ matches.append(detection)
44
+ continue
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
45
 
46
+ # Handle plurals
47
+ if user_input.endswith('s') and label == user_input[:-1]:
48
+ matches.append(detection)
49
+ continue
50
+ if label.endswith('s') and user_input == label[:-1]:
51
+ matches.append(detection)
52
+ continue
53
 
54
+ # Substring matching
55
+ if user_input in label or label in user_input:
56
+ matches.append(detection)
57
+ continue
 
 
 
 
 
 
 
 
 
 
 
58
 
59
+ # Handle common synonyms
60
+ synonyms = {
61
+ 'person': ['human', 'people', 'man', 'woman', 'individual'],
62
+ 'car': ['vehicle', 'automobile', 'auto'],
63
+ 'bike': ['bicycle', 'cycle'],
64
+ 'phone': ['mobile', 'cellphone', 'smartphone'],
65
+ 'tv': ['television', 'telly'],
66
+ 'couch': ['sofa', 'settee'],
67
+ 'bag': ['purse', 'handbag', 'backpack'],
68
+ 'glasses': ['spectacles', 'eyeglasses'],
69
+ 'plane': ['airplane', 'aircraft'],
70
+ 'boat': ['ship', 'vessel'],
71
+ 'dog': ['puppy', 'canine'],
72
+ 'cat': ['kitten', 'feline']
73
+ }
74
+
75
+ # Check if user input matches any synonym
76
+ for main_word, synonym_list in synonyms.items():
77
+ if (user_input == main_word and label in synonym_list) or \
78
+ (user_input in synonym_list and label == main_word):
79
+ matches.append(detection)
80
+ break
81
+
82
+ return matches
83
+
84
+ @spaces.GPU
85
+ def flux_inpainting(image, object_name, guidance_scale=2.5, steps=28):
86
+ """
87
+ Use FLUX.1 Kontext for intelligent object removal
88
+ """
89
+ global flux_pipe
90
+
91
+ try:
92
+ # Load FLUX model if not already loaded
93
+ if flux_pipe is None:
94
+ print("Loading FLUX.1 Kontext model...")
95
+ flux_pipe = load_flux_model()
96
 
97
+ if flux_pipe is None:
98
+ raise Exception("Failed to load FLUX model")
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
99
 
100
+ # Create intelligent removal prompt
101
+ removal_prompt = f"Remove the {object_name} from this image, fill with background that matches the surrounding environment, photorealistic, seamless, high quality"
102
+
103
+ # Use FLUX for contextual editing
104
+ result = flux_pipe(
105
+ image=image.convert("RGB"),
106
+ prompt=removal_prompt,
107
+ guidance_scale=guidance_scale,
108
+ width=image.size[0],
109
+ height=image.size[1],
110
+ num_inference_steps=steps,
111
+ generator=torch.Generator().manual_seed(42),
112
+ ).images[0]
113
+
114
+ return result, True
115
+
116
+ except Exception as e:
117
+ print(f"FLUX inpainting error: {str(e)}")
118
+ return None, False
119
+
120
+ @spaces.GPU
121
+ def remove_objects(image, object_name, guidance_scale, steps):
122
+ """
123
+ Main function to remove any specified object using advanced detection + FLUX inpainting
124
+ """
125
+ try:
126
+ if image is None:
127
+ raise gr.Error("Please upload an image")
128
+
129
+ if not object_name or not object_name.strip():
130
+ raise gr.Error("Please enter the name of the object you want to remove")
131
+
132
+ # Try to get token from multiple sources
133
+ token = os.getenv("HF_TOKEN") or os.getenv("HUGGINGFACE_HUB_TOKEN")
134
+ if not token:
135
+ raise gr.Error("Please provide your Hugging Face token or set HF_TOKEN in Space secrets")
136
+
137
+ # Use FLUX.1 Kontext for intelligent object removal
138
+ print("Using FLUX.1 Kontext for advanced object removal...")
139
+ result_image, flux_success = flux_inpainting(image, object_name, guidance_scale, steps)
140
+
141
+ if flux_success and result_image:
142
+ status_msg = f"✅ Successfully removed '{object_name}' object(s)\n"
143
+ status_msg += f"⚙️ Settings: Guidance={guidance_scale}, Steps={steps}"
144
+ return result_image, status_msg
145
+ else:
146
+ # Fallback: show detection areas
147
+ status_msg = f"⚠️ Inpainting failed, but detection was successful\n"
148
+ status_msg += f"💡 Try adjusting guidance scale or steps, or check GPU availability"
149
+ return result_image, status_msg
150
+
151
+ except Exception as e:
152
+ return image, f"❌ Error: {str(e)}"
153
+
154
+ # Custom CSS for modern chat-like interface
155
+ custom_css = """
156
+ /* Global Styles */
157
+ .gradio-container {
158
+ background: linear-gradient(135deg, #0a0a0a 0%, #1a1a2e 50%, #16213e 100%) !important;
159
+ color: white !important;
160
+ font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, sans-serif !important;
161
+ }
162
+
163
+ /* Header Styling */
164
+ .header-text h1 {
165
+ background: linear-gradient(45deg, #00ff88, #00ccff) !important;
166
+ -webkit-background-clip: text !important;
167
+ -webkit-text-fill-color: transparent !important;
168
+ font-size: 2.5rem !important;
169
+ text-align: center !important;
170
+ margin-bottom: 1rem !important;
171
+ }
172
+
173
+ .header-text p {
174
+ text-align: center !important;
175
+ color: rgba(255, 255, 255, 0.8) !important;
176
+ font-size: 1.1rem !important;
177
+ margin-bottom: 2rem !important;
178
+ }
179
+
180
+ /* Chat Panel Styling */
181
+ .chat-panel {
182
+ background: rgba(20, 20, 30, 0.8) !important;
183
+ backdrop-filter: blur(20px) !important;
184
+ border-radius: 16px !important;
185
+ border: 1px solid rgba(255, 255, 255, 0.1) !important;
186
+ padding: 1.5rem !important;
187
+ margin-bottom: 1rem !important;
188
+ }
189
+
190
+ .chat-panel .gr-form {
191
+ background: transparent !important;
192
+ }
193
+
194
+ /* Input Styling */
195
+ .gr-textbox input, .gr-slider input {
196
+ background: rgba(255, 255, 255, 0.1) !important;
197
+ border: 1px solid rgba(255, 255, 255, 0.3) !important;
198
+ border-radius: 25px !important;
199
+ color: white !important;
200
+ padding: 12px 16px !important;
201
+ }
202
+
203
+ .gr-textbox input:focus, .gr-slider input:focus {
204
+ border-color: #00ff88 !important;
205
+ box-shadow: 0 0 0 2px rgba(0, 255, 136, 0.2) !important;
206
+ }
207
+
208
+ .gr-textbox input::placeholder {
209
+ color: rgba(255, 255, 255, 0.5) !important;
210
+ }
211
+
212
+ /* Button Styling */
213
+ .gr-button {
214
+ background: linear-gradient(135deg, #00ff88, #00ccff) !important;
215
+ border: none !important;
216
+ border-radius: 25px !important;
217
+ color: #000 !important;
218
+ font-weight: 600 !important;
219
+ padding: 12px 24px !important;
220
+ transition: all 0.3s ease !important;
221
+ }
222
+
223
+ .gr-button:hover {
224
+ transform: translateY(-2px) !important;
225
+ box-shadow: 0 8px 25px rgba(0, 255, 136, 0.3) !important;
226
+ }
227
+
228
+ /* Output Panel Styling */
229
+ .output-panel {
230
+ background: rgba(10, 10, 20, 0.8) !important;
231
+ backdrop-filter: blur(20px) !important;
232
+ border-radius: 16px !important;
233
+ border: 1px solid rgba(255, 255, 255, 0.1) !important;
234
+ padding: 1.5rem !important;
235
+ text-align: center !important;
236
+ }
237
+
238
+ .gr-image {
239
+ border-radius: 16px !important;
240
+ border: 2px solid rgba(255, 255, 255, 0.1) !important;
241
+ box-shadow: 0 20px 60px rgba(0, 0, 0, 0.4) !important;
242
+ }
243
+
244
+ /* Status Text Styling */
245
+ .gr-textbox textarea {
246
+ background: rgba(255, 193, 7, 0.1) !important;
247
+ border: 1px solid rgba(255, 193, 7, 0.3) !important;
248
+ color: #ffc107 !important;
249
+ border-radius: 12px !important;
250
+ }
251
+
252
+ /* File Upload Styling */
253
+ .gr-file-upload {
254
+ background: rgba(255, 255, 255, 0.1) !important;
255
+ border: 2px dashed rgba(255, 255, 255, 0.3) !important;
256
+ border-radius: 16px !important;
257
+ color: white !important;
258
+ }
259
+
260
+ /* Examples Styling */
261
+ .gr-examples {
262
+ background: rgba(255, 255, 255, 0.05) !important;
263
+ border-radius: 12px !important;
264
+ padding: 1rem !important;
265
+ }
266
+
267
+ .gr-examples .gr-button {
268
+ background: rgba(255, 255, 255, 0.1) !important;
269
+ color: white !important;
270
+ border: 1px solid rgba(255, 255, 255, 0.3) !important;
271
+ font-size: 0.9rem !important;
272
+ }
273
+
274
+ /* Accordion Styling */
275
+ .gr-accordion {
276
+ background: rgba(255, 255, 255, 0.05) !important;
277
+ border: 1px solid rgba(255, 255, 255, 0.1) !important;
278
+ border-radius: 12px !important;
279
+ }
280
+
281
+ .gr-accordion summary {
282
+ color: white !important;
283
+ font-weight: 500 !important;
284
+ }
285
+
286
+ /* Responsive adjustments */
287
+ @media (max-width: 768px) {
288
+ .gr-row {
289
+ flex-direction: column !important;
290
+ }
291
+
292
+ .header-text h1 {
293
+ font-size: 2rem !important;
294
+ }
295
+ }
296
+ """
297
+
298
+ # Create the Gradio interface with modern styling
299
+ with gr.Blocks(
300
+ title="🚀 AI Object Removal Chat",
301
+ theme=gr.themes.Base(
302
+ primary_hue="emerald",
303
+ secondary_hue="blue",
304
+ neutral_hue="slate",
305
+ font=[gr.themes.GoogleFont("Inter"), "ui-sans-serif", "system-ui", "sans-serif"]
306
+ ),
307
+ css=custom_css,
308
+ fill_height=True
309
+ ) as demo:
310
+
311
+ # Header Section
312
+ with gr.Row():
313
+ with gr.Column():
314
+ gr.HTML("""
315
+ <div class="header-text">
316
+ <h1>🚀 AI Object Removal Chat</h1>
317
+ <p>Upload an image and tell me what object you want to remove with professional results!</p>
318
  </div>
319
+ """)
320
+
321
+ # Main Content Layout
322
+ with gr.Row(equal_height=True):
323
+ # Left Panel - Chat-like Interface
324
+ with gr.Column(scale=2, elem_classes=["chat-panel"]):
325
+ gr.Markdown("### 💬 Chat Interface")
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
326
 
327
+ # Image Upload
328
+ input_image = gr.Image(
329
+ label="📷 Upload Your Image",
330
+ type="pil",
331
+ height=250,
332
+ container=True
333
+ )
334
 
335
+ # Object Input
336
+ object_name = gr.Textbox(
337
+ label="🎯 What should I remove?",
338
+ placeholder="Type any object name (e.g., person, car, dog, bottle, tree, sign...)",
339
+ value="person",
340
+ container=True,
341
+ info="💡 I understand synonyms and variations!"
342
+ )
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
343
 
344
+ # Quick Examples
345
+ with gr.Row():
346
+ example_buttons = []
347
+ examples = ["person", "car", "dog", "cat", "bottle", "chair", "tree", "sign"]
348
+ for example in examples:
349
+ btn = gr.Button(example, size="sm", variant="secondary")
350
+ btn.click(lambda x=example: x, outputs=object_name)
351
+ example_buttons.append(btn)
352
 
353
+ # Advanced Settings
354
+ with gr.Accordion("⚙️ Advanced Settings", open=False):
355
+ guidance_scale = gr.Slider(
356
+ minimum=1.0,
357
+ maximum=10.0,
358
+ value=2.5,
359
+ step=0.1,
360
+ label="🎯 Guidance Scale",
361
+ info="Higher = more faithful to prompt, lower = more creative"
362
+ )
363
+
364
+ steps = gr.Slider(
365
+ minimum=10,
366
+ maximum=50,
367
+ value=28,
368
+ step=2,
369
+ label="🔄 Processing Steps",
370
+ info="More steps = higher quality but slower processing"
371
+ )
372
 
373
+ # Process Button
374
+ process_btn = gr.Button(
375
+ "🚀 Remove Object",
376
+ variant="primary",
377
+ size="lg",
378
+ elem_id="process-btn"
379
+ )
380
+
381
+ # Right Panel - Output
382
+ with gr.Column(scale=3, elem_classes=["output-panel"]):
383
+ gr.Markdown("### 🖼️ Result")
 
 
 
 
 
 
 
 
 
384
 
385
+ output_image = gr.Image(
386
+ label="Processed Image",
387
+ type="pil",
388
+ height=400,
389
+ container=True,
390
+ interactive=False
391
+ )
392
 
393
+ status_text = gr.Textbox(
394
+ label="📊 Status & Information",
395
+ max_lines=4,
396
+ interactive=False,
397
+ container=True
398
+ )
399
+
400
+ # Chat-like Examples
401
+ gr.Examples(
402
+ examples=[
403
+ ["https://images.unsplash.com/photo-1544005313-94ddf0286df2?w=300", "person"],
404
+ ["https://images.unsplash.com/photo-1449824913935-59a10b8d2000?w=300", "car"],
405
+ ["https://images.unsplash.com/photo-1552053831-71594a27632d?w=300", "dog"]
406
+ ],
407
+ inputs=[input_image, object_name],
408
+ label="💡 Try these examples"
409
+ )
410
+
411
+ # Event Handlers
412
+ process_btn.click(
413
+ fn=remove_objects,
414
+ inputs=[input_image, object_name, guidance_scale, steps],
415
+ outputs=[output_image, status_text]
416
+ )
417
+
418
+ # Auto-process on Enter key
419
+ object_name.submit(
420
+ fn=remove_objects,
421
+ inputs=[input_image, object_name, guidance_scale, steps],
422
+ outputs=[output_image, status_text]
423
+ )
424
+
425
+ if __name__ == "__main__":
426
+ demo.launch()